agent-inspect 6.7.5 → 6.9.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/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/docs/CLI.md +10 -3
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-TO4VENHV.mjs → chunk-WQ5XMFH2.mjs} +109 -17
- package/packages/cli/dist/chunk-WQ5XMFH2.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +357 -66
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +252 -53
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-Z5W27YCW.mjs → src-C4EUHTER.mjs} +3 -3
- package/packages/cli/dist/{src-Z5W27YCW.mjs.map → src-C4EUHTER.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +15 -6
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +4 -1
- package/packages/core/dist/advanced.d.ts +4 -1
- package/packages/core/dist/advanced.mjs +3 -2
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.cjs +105 -14
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.d.cts +2 -2
- package/packages/core/dist/checks.d.ts +2 -2
- package/packages/core/dist/checks.mjs +1 -1
- package/packages/core/dist/{chunk-EQVXDGGU.mjs → chunk-QT6CQ2XA.mjs} +107 -16
- package/packages/core/dist/chunk-QT6CQ2XA.mjs.map +1 -0
- package/packages/core/dist/{index-BblJEdYZ.d.ts → index-BO7l0iAe.d.ts} +44 -1
- package/packages/core/dist/{index-DyKk7iR9.d.cts → index-mdFcxSOR.d.cts} +44 -1
- package/packages/cli/dist/chunk-TO4VENHV.mjs.map +0 -1
- package/packages/core/dist/chunk-EQVXDGGU.mjs.map +0 -1
|
@@ -4105,8 +4105,9 @@ function buildBundleSummaryMarkdown(parts) {
|
|
|
4105
4105
|
""
|
|
4106
4106
|
];
|
|
4107
4107
|
for (const run of checks2.runs) {
|
|
4108
|
+
const source = run.sourceStatus !== void 0 && run.sourceStatus !== run.status ? `; source ${run.sourceStatus}` : "";
|
|
4108
4109
|
lines.push(
|
|
4109
|
-
`- \`${run.runId}\`: ${run.status} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
|
|
4110
|
+
`- \`${run.runId}\`: artifact ${run.status}${source} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
|
|
4110
4111
|
);
|
|
4111
4112
|
}
|
|
4112
4113
|
lines.push("", "## Redaction", "", `Total findings: ${redaction.totalFindings}`, "");
|
|
@@ -4691,7 +4692,11 @@ function normalizeFinding(rule, finding) {
|
|
|
4691
4692
|
message: finding.message,
|
|
4692
4693
|
...finding.expected !== void 0 ? { expected: finding.expected } : {},
|
|
4693
4694
|
...finding.actual !== void 0 ? { actual: finding.actual } : {},
|
|
4694
|
-
evidence: [...finding.evidence ?? []]
|
|
4695
|
+
evidence: [...finding.evidence ?? []],
|
|
4696
|
+
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
4697
|
+
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
4698
|
+
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
4699
|
+
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
4695
4700
|
};
|
|
4696
4701
|
}
|
|
4697
4702
|
function summarize(findings, diagnostics) {
|
|
@@ -4749,15 +4754,19 @@ function eventEvidence(event, path37) {
|
|
|
4749
4754
|
function runEvidence(run) {
|
|
4750
4755
|
return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
|
|
4751
4756
|
}
|
|
4752
|
-
function failFinding(ruleId, message, evidence, expected, actual) {
|
|
4757
|
+
function failFinding(ruleId, message, evidence, expected, actual, meta2) {
|
|
4753
4758
|
return {
|
|
4754
4759
|
ruleId,
|
|
4755
|
-
severity: "error",
|
|
4756
|
-
status: "fail",
|
|
4760
|
+
severity: meta2?.severity ?? "error",
|
|
4761
|
+
status: meta2?.status ?? "fail",
|
|
4757
4762
|
message,
|
|
4758
4763
|
...expected !== void 0 ? { expected } : {},
|
|
4759
4764
|
...actual !== void 0 ? { actual } : {},
|
|
4760
|
-
evidence: [...evidence]
|
|
4765
|
+
evidence: [...evidence],
|
|
4766
|
+
...meta2?.category !== void 0 ? { category: meta2.category } : {},
|
|
4767
|
+
...meta2?.confidence !== void 0 ? { confidence: meta2.confidence } : {},
|
|
4768
|
+
...meta2?.detector !== void 0 ? { detector: meta2.detector } : {},
|
|
4769
|
+
...meta2?.action !== void 0 ? { action: meta2.action } : {}
|
|
4761
4770
|
};
|
|
4762
4771
|
}
|
|
4763
4772
|
function toolName(event) {
|
|
@@ -4878,6 +4887,19 @@ function isRawContentKey(key, forbiddenKeys) {
|
|
|
4878
4887
|
const normalized = normalizedKey(key);
|
|
4879
4888
|
return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
|
|
4880
4889
|
}
|
|
4890
|
+
function isSafeRawContentMetricPath(path37, key, safePathPrefixes) {
|
|
4891
|
+
const leaf = normalizedKey(key ?? lastPathSegment(path37));
|
|
4892
|
+
if (!SAFE_USAGE_LEAF_KEYS.has(leaf)) return false;
|
|
4893
|
+
const parts = path37.split(".").filter(Boolean);
|
|
4894
|
+
if (parts.length < 2) return false;
|
|
4895
|
+
const parent = parts[parts.length - 2] ?? "";
|
|
4896
|
+
const parentNorm = normalizedKey(parent);
|
|
4897
|
+
return safePathPrefixes.some((prefix) => parentNorm === normalizedKey(prefix));
|
|
4898
|
+
}
|
|
4899
|
+
function isRawContentPath(path37, key, forbiddenKeys, safePathPrefixes) {
|
|
4900
|
+
if (isSafeRawContentMetricPath(path37, key, safePathPrefixes)) return false;
|
|
4901
|
+
return isRawContentKey(key ?? lastPathSegment(path37), forbiddenKeys);
|
|
4902
|
+
}
|
|
4881
4903
|
function parentMarkedUnresolved(event) {
|
|
4882
4904
|
if (booleanAttr(event, [
|
|
4883
4905
|
"parentUnresolved",
|
|
@@ -5517,7 +5539,13 @@ function createSafetyRedactionRule(options = {}) {
|
|
|
5517
5539
|
`Sensitive-looking field at ${entry.path} is not redacted.`,
|
|
5518
5540
|
[eventEvidence(event, entry.path)],
|
|
5519
5541
|
"redaction marker",
|
|
5520
|
-
{ path: entry.path, valueType: valueType(entry.value) }
|
|
5542
|
+
{ path: entry.path, valueType: valueType(entry.value) },
|
|
5543
|
+
{
|
|
5544
|
+
category: "credential",
|
|
5545
|
+
confidence: "high",
|
|
5546
|
+
detector: "safety.redaction",
|
|
5547
|
+
action: "redact"
|
|
5548
|
+
}
|
|
5521
5549
|
)
|
|
5522
5550
|
);
|
|
5523
5551
|
}
|
|
@@ -5528,6 +5556,7 @@ function createSafetyRedactionRule(options = {}) {
|
|
|
5528
5556
|
}
|
|
5529
5557
|
function createSafetyRawContentRule(options = {}) {
|
|
5530
5558
|
const forbiddenKeys = options.forbiddenKeys ?? DEFAULT_RAW_CONTENT_KEYS;
|
|
5559
|
+
const safePathPrefixes = options.safePathPrefixes ?? DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES;
|
|
5531
5560
|
return {
|
|
5532
5561
|
id: "safety.rawPrompt",
|
|
5533
5562
|
category: "safety",
|
|
@@ -5537,14 +5566,20 @@ function createSafetyRawContentRule(options = {}) {
|
|
|
5537
5566
|
for (const event of context.events) {
|
|
5538
5567
|
for (const entry of eventValueEntries(event, { includeSummaries: options.includeSummaries })) {
|
|
5539
5568
|
const key = entry.key ?? lastPathSegment(entry.path);
|
|
5540
|
-
if (!
|
|
5569
|
+
if (!isRawContentPath(entry.path, key, forbiddenKeys, safePathPrefixes)) continue;
|
|
5541
5570
|
findings.push(
|
|
5542
5571
|
failFinding(
|
|
5543
5572
|
"safety.rawPrompt",
|
|
5544
5573
|
`Raw content-like field ${entry.path} is present.`,
|
|
5545
5574
|
[eventEvidence(event, entry.path)],
|
|
5546
5575
|
"metadata-only trace fields",
|
|
5547
|
-
{ path: entry.path, valueType: valueType(entry.value) }
|
|
5576
|
+
{ path: entry.path, valueType: valueType(entry.value) },
|
|
5577
|
+
{
|
|
5578
|
+
category: "raw-content",
|
|
5579
|
+
confidence: "high",
|
|
5580
|
+
detector: "safety.rawPrompt",
|
|
5581
|
+
action: "redact-or-omit"
|
|
5582
|
+
}
|
|
5548
5583
|
)
|
|
5549
5584
|
);
|
|
5550
5585
|
}
|
|
@@ -5576,7 +5611,13 @@ function createSafetySecretPatternRule(options = {}) {
|
|
|
5576
5611
|
`Secret-like pattern ${pattern.id} matched at ${entry.path}.`,
|
|
5577
5612
|
[eventEvidence(event, entry.path)],
|
|
5578
5613
|
"no secret-like strings",
|
|
5579
|
-
{ pattern: pattern.id, path: entry.path }
|
|
5614
|
+
{ pattern: pattern.id, path: entry.path },
|
|
5615
|
+
{
|
|
5616
|
+
category: "credential",
|
|
5617
|
+
confidence: "high",
|
|
5618
|
+
detector: pattern.id,
|
|
5619
|
+
action: "redact"
|
|
5620
|
+
}
|
|
5580
5621
|
)
|
|
5581
5622
|
);
|
|
5582
5623
|
break;
|
|
@@ -5603,7 +5644,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
5603
5644
|
`String at ${entry.path} exceeds ${options.maxStringLength} characters.`,
|
|
5604
5645
|
[eventEvidence(event, entry.path)],
|
|
5605
5646
|
{ maxStringLength: options.maxStringLength },
|
|
5606
|
-
{ path: entry.path, length: entry.value.length }
|
|
5647
|
+
{ path: entry.path, length: entry.value.length },
|
|
5648
|
+
{
|
|
5649
|
+
category: "size",
|
|
5650
|
+
confidence: "high",
|
|
5651
|
+
detector: "safety.oversizedAttribute",
|
|
5652
|
+
action: "truncate-or-omit"
|
|
5653
|
+
}
|
|
5607
5654
|
)
|
|
5608
5655
|
);
|
|
5609
5656
|
}
|
|
@@ -5614,7 +5661,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
5614
5661
|
`Array at ${entry.path} exceeds ${options.maxArrayLength} items.`,
|
|
5615
5662
|
[eventEvidence(event, entry.path)],
|
|
5616
5663
|
{ maxArrayLength: options.maxArrayLength },
|
|
5617
|
-
{ path: entry.path, length: entry.value.length }
|
|
5664
|
+
{ path: entry.path, length: entry.value.length },
|
|
5665
|
+
{
|
|
5666
|
+
category: "size",
|
|
5667
|
+
confidence: "high",
|
|
5668
|
+
detector: "safety.oversizedAttribute",
|
|
5669
|
+
action: "truncate-or-omit"
|
|
5670
|
+
}
|
|
5618
5671
|
)
|
|
5619
5672
|
);
|
|
5620
5673
|
}
|
|
@@ -5625,7 +5678,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
5625
5678
|
`Object at ${entry.path} exceeds ${options.maxObjectKeys} keys.`,
|
|
5626
5679
|
[eventEvidence(event, entry.path)],
|
|
5627
5680
|
{ maxObjectKeys: options.maxObjectKeys },
|
|
5628
|
-
{ path: entry.path, keys: Object.keys(entry.value).length }
|
|
5681
|
+
{ path: entry.path, keys: Object.keys(entry.value).length },
|
|
5682
|
+
{
|
|
5683
|
+
category: "size",
|
|
5684
|
+
confidence: "high",
|
|
5685
|
+
detector: "safety.oversizedAttribute",
|
|
5686
|
+
action: "truncate-or-omit"
|
|
5687
|
+
}
|
|
5629
5688
|
)
|
|
5630
5689
|
);
|
|
5631
5690
|
}
|
|
@@ -5638,7 +5697,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
5638
5697
|
`Value at ${entry.path} exceeds ${options.maxSerializedBytes} serialized bytes.`,
|
|
5639
5698
|
[eventEvidence(event, entry.path)],
|
|
5640
5699
|
{ maxSerializedBytes: options.maxSerializedBytes },
|
|
5641
|
-
{ path: entry.path, bytes }
|
|
5700
|
+
{ path: entry.path, bytes },
|
|
5701
|
+
{
|
|
5702
|
+
category: "size",
|
|
5703
|
+
confidence: "high",
|
|
5704
|
+
detector: "safety.oversizedAttribute",
|
|
5705
|
+
action: "truncate-or-omit"
|
|
5706
|
+
}
|
|
5642
5707
|
)
|
|
5643
5708
|
);
|
|
5644
5709
|
}
|
|
@@ -5855,7 +5920,7 @@ function runTraceChecks(input3, options = {}) {
|
|
|
5855
5920
|
diagnostics
|
|
5856
5921
|
};
|
|
5857
5922
|
}
|
|
5858
|
-
var SEVERITY_RANK, STATUS_RANK, CONFIDENCE_RANK, DEFAULT_SENSITIVE_KEYS, DEFAULT_RAW_CONTENT_KEYS, DEFAULT_SECRET_PATTERNS;
|
|
5923
|
+
var SEVERITY_RANK, STATUS_RANK, CONFIDENCE_RANK, DEFAULT_SENSITIVE_KEYS, DEFAULT_RAW_CONTENT_KEYS, DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES, SAFE_USAGE_LEAF_KEYS, DEFAULT_SECRET_PATTERNS;
|
|
5859
5924
|
var init_checks2 = __esm({
|
|
5860
5925
|
"packages/core/src/checks/index.ts"() {
|
|
5861
5926
|
init_outcomes();
|
|
@@ -5905,8 +5970,35 @@ var init_checks2 = __esm({
|
|
|
5905
5970
|
"toolinput",
|
|
5906
5971
|
"tool_input",
|
|
5907
5972
|
"tooloutput",
|
|
5908
|
-
"tool_output"
|
|
5973
|
+
"tool_output",
|
|
5974
|
+
// Framework / agent metadata that carries user or task text
|
|
5975
|
+
"currenttask",
|
|
5976
|
+
"current_task",
|
|
5977
|
+
"task",
|
|
5978
|
+
"userinput",
|
|
5979
|
+
"user_input",
|
|
5980
|
+
"requesttext",
|
|
5981
|
+
"request_text",
|
|
5982
|
+
"conversationtext",
|
|
5983
|
+
"conversation_text"
|
|
5909
5984
|
];
|
|
5985
|
+
DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = ["tokenUsage", "usage"];
|
|
5986
|
+
SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
|
|
5987
|
+
"input",
|
|
5988
|
+
"output",
|
|
5989
|
+
"total",
|
|
5990
|
+
"cached",
|
|
5991
|
+
"input_tokens",
|
|
5992
|
+
"inputtokens",
|
|
5993
|
+
"output_tokens",
|
|
5994
|
+
"outputtokens",
|
|
5995
|
+
"total_tokens",
|
|
5996
|
+
"totaltokens",
|
|
5997
|
+
"prompt_tokens",
|
|
5998
|
+
"prompttokens",
|
|
5999
|
+
"completion_tokens",
|
|
6000
|
+
"completiontokens"
|
|
6001
|
+
]);
|
|
5910
6002
|
DEFAULT_SECRET_PATTERNS = [
|
|
5911
6003
|
{ id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
|
|
5912
6004
|
{ id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
|
|
@@ -10069,7 +10161,7 @@ var init_src = __esm({
|
|
|
10069
10161
|
});
|
|
10070
10162
|
|
|
10071
10163
|
// package.json
|
|
10072
|
-
var version = "6.
|
|
10164
|
+
var version = "6.9.0";
|
|
10073
10165
|
|
|
10074
10166
|
// packages/cli/src/list.ts
|
|
10075
10167
|
init_advanced();
|
|
@@ -14450,7 +14542,12 @@ var SHARE_PROFILE_EXTRA_KEYS2 = [
|
|
|
14450
14542
|
"organizationId",
|
|
14451
14543
|
"traceId",
|
|
14452
14544
|
"spanId",
|
|
14453
|
-
"parentSpanId"
|
|
14545
|
+
"parentSpanId",
|
|
14546
|
+
"currentTask",
|
|
14547
|
+
"task",
|
|
14548
|
+
"userInput",
|
|
14549
|
+
"requestText",
|
|
14550
|
+
"conversationText"
|
|
14454
14551
|
];
|
|
14455
14552
|
var STRICT_PROFILE_EXTRA_KEYS2 = [
|
|
14456
14553
|
"prompt",
|
|
@@ -14518,6 +14615,61 @@ function passesLuhn(value) {
|
|
|
14518
14615
|
}
|
|
14519
14616
|
return sum % 10 === 0;
|
|
14520
14617
|
}
|
|
14618
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
14619
|
+
var EPOCH_MS_RE = /^1[0-9]{12}$/;
|
|
14620
|
+
var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
|
|
14621
|
+
function pathSuggestsNonCard(path37) {
|
|
14622
|
+
const normalized = path37.toLowerCase().replace(/[^a-z0-9._]/g, "");
|
|
14623
|
+
return /(^|\.)(tokenusage|usage)(\.|$)/.test(normalized) || /(startedat|endedat|durationms|timestamp|createdat|updatedat)(\.|$)/.test(normalized) || /(^|\.)(runid|traceid|spanid|eventid|sessionid|userid|parentid|requestid|correlationid)(\.|$)/.test(
|
|
14624
|
+
normalized
|
|
14625
|
+
) || /(^|\.)ts(\.|$)/.test(normalized);
|
|
14626
|
+
}
|
|
14627
|
+
function isUuidLike(value) {
|
|
14628
|
+
return UUID_RE.test(value.trim());
|
|
14629
|
+
}
|
|
14630
|
+
function isPlausibleCardCandidate(candidate, fullValue) {
|
|
14631
|
+
const digits = digitsOnly(candidate);
|
|
14632
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
14633
|
+
if (!passesLuhn(candidate)) return false;
|
|
14634
|
+
const trimmed = fullValue.trim();
|
|
14635
|
+
if (isUuidLike(trimmed)) return false;
|
|
14636
|
+
if (EPOCH_MS_RE.test(digits) && digitsOnly(trimmed) === digits && /^\d+$/.test(trimmed)) {
|
|
14637
|
+
return false;
|
|
14638
|
+
}
|
|
14639
|
+
const start = fullValue.indexOf(candidate);
|
|
14640
|
+
if (start >= 0) {
|
|
14641
|
+
const before = fullValue[start - 1];
|
|
14642
|
+
const after = fullValue[start + candidate.length];
|
|
14643
|
+
if (before && /[A-Za-z0-9_]/.test(before)) return false;
|
|
14644
|
+
if (after && /[A-Za-z0-9_]/.test(after)) return false;
|
|
14645
|
+
}
|
|
14646
|
+
return true;
|
|
14647
|
+
}
|
|
14648
|
+
function looksLikePathOrPackageOrUrl(value) {
|
|
14649
|
+
const trimmed = value.trim();
|
|
14650
|
+
if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(trimmed)) return true;
|
|
14651
|
+
if (/^webpack:/i.test(trimmed)) return true;
|
|
14652
|
+
if (/^@[A-Za-z0-9_.-]+\/[A-Za-z0-9_.@/-]+$/.test(trimmed)) return true;
|
|
14653
|
+
if (/^[A-Za-z]:[\\/]/.test(trimmed) || /^[/\\]/.test(trimmed)) return true;
|
|
14654
|
+
if (/[\\/]/.test(trimmed) && !/\s/.test(trimmed)) return true;
|
|
14655
|
+
return false;
|
|
14656
|
+
}
|
|
14657
|
+
function emailMatchIsPathAdjacent(value, index, length) {
|
|
14658
|
+
const before = value[index - 1];
|
|
14659
|
+
const after = value[index + length];
|
|
14660
|
+
return before === "/" || before === "\\" || after === "/" || after === "\\";
|
|
14661
|
+
}
|
|
14662
|
+
function valueContainsEmail(value) {
|
|
14663
|
+
if (looksLikePathOrPackageOrUrl(value)) return false;
|
|
14664
|
+
EMAIL_RE.lastIndex = 0;
|
|
14665
|
+
for (const match of value.matchAll(EMAIL_RE)) {
|
|
14666
|
+
const text = match[0] ?? "";
|
|
14667
|
+
const index = match.index ?? 0;
|
|
14668
|
+
if (emailMatchIsPathAdjacent(value, index, text.length)) continue;
|
|
14669
|
+
return true;
|
|
14670
|
+
}
|
|
14671
|
+
return false;
|
|
14672
|
+
}
|
|
14521
14673
|
var credentialDetectors = [
|
|
14522
14674
|
patternDetector({
|
|
14523
14675
|
id: "value.authorizationHeader",
|
|
@@ -14565,10 +14717,12 @@ var credentialDetectors = [
|
|
|
14565
14717
|
matchKind: "value",
|
|
14566
14718
|
detect(input3) {
|
|
14567
14719
|
if (typeof input3.value !== "string") return [];
|
|
14720
|
+
if (pathSuggestsNonCard(input3.path)) return [];
|
|
14721
|
+
if (isUuidLike(input3.value)) return [];
|
|
14568
14722
|
const candidatePattern = /(?:\d[ -]?){13,19}/g;
|
|
14569
14723
|
for (const match of input3.value.matchAll(candidatePattern)) {
|
|
14570
14724
|
const candidate = match[0] ?? "";
|
|
14571
|
-
if (
|
|
14725
|
+
if (isPlausibleCardCandidate(candidate, input3.value)) {
|
|
14572
14726
|
return [{ action: "replace", severity: "error", matchKind: "value" }];
|
|
14573
14727
|
}
|
|
14574
14728
|
}
|
|
@@ -14577,10 +14731,15 @@ var credentialDetectors = [
|
|
|
14577
14731
|
}
|
|
14578
14732
|
];
|
|
14579
14733
|
var identifierDetectors = [
|
|
14580
|
-
|
|
14734
|
+
{
|
|
14581
14735
|
id: "value.email",
|
|
14582
|
-
|
|
14583
|
-
|
|
14736
|
+
severity: "warning",
|
|
14737
|
+
matchKind: "value",
|
|
14738
|
+
detect(input3) {
|
|
14739
|
+
if (typeof input3.value !== "string") return [];
|
|
14740
|
+
return valueContainsEmail(input3.value) ? [{ action: "replace", severity: "warning", matchKind: "value" }] : [];
|
|
14741
|
+
}
|
|
14742
|
+
},
|
|
14584
14743
|
patternDetector({
|
|
14585
14744
|
id: "value.phone",
|
|
14586
14745
|
pattern: /\b(?:\+\d{1,3}[\s.-]?)?(?:\(?\d{3}\)?[\s.-])\d{3}[\s.-]\d{4}\b/
|
|
@@ -14904,6 +15063,16 @@ function redactJsonlText(content, profile) {
|
|
|
14904
15063
|
};
|
|
14905
15064
|
}
|
|
14906
15065
|
function redactDocument(content, profile) {
|
|
15066
|
+
const trimmed = content.trim();
|
|
15067
|
+
if (trimmed.startsWith("{")) {
|
|
15068
|
+
try {
|
|
15069
|
+
const parsed = JSON.parse(trimmed);
|
|
15070
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed)) {
|
|
15071
|
+
return redactJsonlText(content, profile);
|
|
15072
|
+
}
|
|
15073
|
+
} catch {
|
|
15074
|
+
}
|
|
15075
|
+
}
|
|
14907
15076
|
try {
|
|
14908
15077
|
return redactJsonText(content, profile);
|
|
14909
15078
|
} catch {
|
|
@@ -18399,8 +18568,6 @@ async function evalCommand(target, options = {}, stdin = process.stdin) {
|
|
|
18399
18568
|
else if (options.markdown) console.log(renderEvalMarkdown(result).trimEnd());
|
|
18400
18569
|
else printHuman2(result);
|
|
18401
18570
|
}
|
|
18402
|
-
|
|
18403
|
-
// packages/cli/src/safety.ts
|
|
18404
18571
|
var BEST_EFFORT_NOTE = "Best-effort local safety verification only; not a compliance, privacy, security, or regulatory certification.";
|
|
18405
18572
|
var DEFAULT_MAX_STRING_LENGTH = 16384;
|
|
18406
18573
|
var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
|
|
@@ -18458,7 +18625,7 @@ function resultFromParts(parts) {
|
|
|
18458
18625
|
const diagnostics = [...parts.diagnostics ?? []];
|
|
18459
18626
|
const warnings = [...parts.warnings ?? []];
|
|
18460
18627
|
const unsupportedFields = [...parts.unsupportedFields ?? []];
|
|
18461
|
-
const status = statusFrom(findings, diagnostics);
|
|
18628
|
+
const status = parts.status ?? statusFrom(findings, diagnostics);
|
|
18462
18629
|
return {
|
|
18463
18630
|
ok: status === "SAFE" || status === "SAFE WITH WARNINGS",
|
|
18464
18631
|
command: parts.command,
|
|
@@ -18474,7 +18641,17 @@ function resultFromParts(parts) {
|
|
|
18474
18641
|
diagnostics,
|
|
18475
18642
|
warnings,
|
|
18476
18643
|
unsupportedFields,
|
|
18477
|
-
note: BEST_EFFORT_NOTE
|
|
18644
|
+
note: BEST_EFFORT_NOTE,
|
|
18645
|
+
...parts.sourceAssessment !== void 0 ? { sourceAssessment: parts.sourceAssessment } : {},
|
|
18646
|
+
...parts.artifactAssessment !== void 0 ? { artifactAssessment: parts.artifactAssessment } : {},
|
|
18647
|
+
...parts.redactionSummary !== void 0 ? { redactionSummary: parts.redactionSummary } : {}
|
|
18648
|
+
};
|
|
18649
|
+
}
|
|
18650
|
+
function layerFromResult(result) {
|
|
18651
|
+
return {
|
|
18652
|
+
status: result.status,
|
|
18653
|
+
summary: result.summary,
|
|
18654
|
+
findings: result.findings
|
|
18478
18655
|
};
|
|
18479
18656
|
}
|
|
18480
18657
|
function readErrorResult3(command, error) {
|
|
@@ -18538,6 +18715,21 @@ function flattenNodes2(nodes) {
|
|
|
18538
18715
|
function detectorSeverity(finding) {
|
|
18539
18716
|
return finding.severity;
|
|
18540
18717
|
}
|
|
18718
|
+
function classifyRedactionDetector(detector) {
|
|
18719
|
+
if (detector === "value.creditCard" || detector === "value.email" || detector === "value.phone") {
|
|
18720
|
+
return { category: "personal-data", confidence: "high" };
|
|
18721
|
+
}
|
|
18722
|
+
if (detector === "value.ipv4" || detector === "value.ipv6") {
|
|
18723
|
+
return { category: "identifier", confidence: "medium" };
|
|
18724
|
+
}
|
|
18725
|
+
if (detector.startsWith("value.") && (detector.includes("Token") || detector.includes("Key") || detector.includes("jwt") || detector.includes("authorization") || detector.includes("bearer") || detector.includes("cookie") || detector.includes("privateKey") || detector.includes("github") || detector.includes("aws") || detector.includes("provider"))) {
|
|
18726
|
+
return { category: "credential", confidence: "high" };
|
|
18727
|
+
}
|
|
18728
|
+
if (detector.startsWith("key.")) {
|
|
18729
|
+
return { category: "credential", confidence: "medium" };
|
|
18730
|
+
}
|
|
18731
|
+
return { category: "credential", confidence: "medium" };
|
|
18732
|
+
}
|
|
18541
18733
|
function redactionDetectorFindings(read, runId) {
|
|
18542
18734
|
const runs = runId === void 0 ? read.runs : read.runs.filter((run) => run.runId === runId);
|
|
18543
18735
|
const out = [];
|
|
@@ -18548,6 +18740,7 @@ function redactionDetectorFindings(read, runId) {
|
|
|
18548
18740
|
const result = redact(attrs, { profile: "share" });
|
|
18549
18741
|
for (const finding of result.findings) {
|
|
18550
18742
|
if (finding.action === "keep") continue;
|
|
18743
|
+
const taxonomy = classifyRedactionDetector(finding.detector);
|
|
18551
18744
|
out.push({
|
|
18552
18745
|
ruleId: "safety.redactDetector",
|
|
18553
18746
|
severity: detectorSeverity(finding),
|
|
@@ -18565,7 +18758,11 @@ function redactionDetectorFindings(read, runId) {
|
|
|
18565
18758
|
...node.event.status !== void 0 ? { status: node.event.status } : {},
|
|
18566
18759
|
path: `attributes.${finding.path.replace(/^\$\.?/, "")}`
|
|
18567
18760
|
}
|
|
18568
|
-
]
|
|
18761
|
+
],
|
|
18762
|
+
category: taxonomy.category,
|
|
18763
|
+
confidence: taxonomy.confidence,
|
|
18764
|
+
detector: finding.detector,
|
|
18765
|
+
action: finding.action
|
|
18569
18766
|
});
|
|
18570
18767
|
}
|
|
18571
18768
|
}
|
|
@@ -18581,13 +18778,49 @@ function exitCodeFor3(result) {
|
|
|
18581
18778
|
if (result.status === "UNSAFE") return 1;
|
|
18582
18779
|
return 2;
|
|
18583
18780
|
}
|
|
18584
|
-
function
|
|
18585
|
-
|
|
18781
|
+
function explainFinding(finding, blocksBundle) {
|
|
18782
|
+
const path37 = finding.evidence[0]?.path ?? "(unknown path)";
|
|
18783
|
+
const category = finding.category ?? "structure";
|
|
18784
|
+
const confidence = finding.confidence ?? "medium";
|
|
18785
|
+
const detector = finding.detector ?? finding.ruleId;
|
|
18786
|
+
const action = finding.action ?? "review";
|
|
18787
|
+
const redactionHint = category === "credential" || category === "personal-data" || category === "raw-content" || action.includes("redact") ? "Usually removable by share/strict redaction before bundling." : "May require omitting the field, lowering limits, or an explicit local override.";
|
|
18788
|
+
return [
|
|
18789
|
+
` Matched: detector=${detector}; path=${path37}; category=${category}`,
|
|
18790
|
+
` Why: ${finding.message}`,
|
|
18791
|
+
` Confidence: ${confidence}`,
|
|
18792
|
+
` Redaction: ${redactionHint}`,
|
|
18793
|
+
` Override: configure a custom redaction/detector rule locally (see docs/SAFETY-POLICY.md); do not weaken defaults globally.`,
|
|
18794
|
+
` Bundle gate: ${blocksBundle ? "blocks share-safe bundle unless --allow-unsafe" : "does not block by itself (warning/info)"}`
|
|
18795
|
+
];
|
|
18796
|
+
}
|
|
18797
|
+
function findingExplanation(finding) {
|
|
18798
|
+
const blocks = finding.severity === "error" || finding.status === "fail";
|
|
18799
|
+
return {
|
|
18800
|
+
ruleId: finding.ruleId,
|
|
18801
|
+
detector: finding.detector ?? finding.ruleId,
|
|
18802
|
+
path: finding.evidence[0]?.path,
|
|
18803
|
+
category: finding.category,
|
|
18804
|
+
confidence: finding.confidence,
|
|
18805
|
+
action: finding.action,
|
|
18806
|
+
blocksBundle: blocks,
|
|
18807
|
+
// Never include matched secret/PII values.
|
|
18808
|
+
message: finding.message
|
|
18809
|
+
};
|
|
18586
18810
|
}
|
|
18587
|
-
function printHuman3(result) {
|
|
18811
|
+
function printHuman3(result, explain = false) {
|
|
18588
18812
|
console.log(`Safety status: ${result.status}`);
|
|
18589
18813
|
console.log(`Format: ${result.format}`);
|
|
18590
18814
|
if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
|
|
18815
|
+
if (result.sourceAssessment !== void 0 && result.artifactAssessment !== void 0) {
|
|
18816
|
+
console.log(`Source assessment: ${result.sourceAssessment.status}`);
|
|
18817
|
+
console.log(`Artifact assessment: ${result.artifactAssessment.status}`);
|
|
18818
|
+
if (result.redactionSummary !== void 0) {
|
|
18819
|
+
console.log(
|
|
18820
|
+
`Redaction: profile=${result.redactionSummary.profile}, findings=${result.redactionSummary.findings}`
|
|
18821
|
+
);
|
|
18822
|
+
}
|
|
18823
|
+
}
|
|
18591
18824
|
console.log(
|
|
18592
18825
|
`Summary: ${result.summary.findings} finding(s), ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
|
|
18593
18826
|
);
|
|
@@ -18596,45 +18829,82 @@ function printHuman3(result) {
|
|
|
18596
18829
|
}
|
|
18597
18830
|
for (const finding of result.findings) {
|
|
18598
18831
|
const path37 = finding.evidence[0]?.path;
|
|
18599
|
-
|
|
18832
|
+
const taxonomy = finding.category !== void 0 || finding.confidence !== void 0 ? ` [${[finding.category, finding.confidence].filter(Boolean).join("/")}]` : "";
|
|
18833
|
+
console.log(`- ${finding.ruleId}: ${finding.message}${path37 ? ` (${path37})` : ""}${taxonomy}`);
|
|
18834
|
+
if (explain) {
|
|
18835
|
+
const blocks = finding.severity === "error" || finding.status === "fail";
|
|
18836
|
+
for (const line of explainFinding(finding, blocks)) {
|
|
18837
|
+
console.log(line);
|
|
18838
|
+
}
|
|
18839
|
+
}
|
|
18600
18840
|
}
|
|
18601
18841
|
console.log(`Note: ${result.note}`);
|
|
18602
18842
|
}
|
|
18843
|
+
function printJson3(result, explain = false) {
|
|
18844
|
+
const payload = explain === true ? { ...result, explanations: result.findings.map(findingExplanation) } : result;
|
|
18845
|
+
console.log(JSON.stringify(stable4(payload), null, 2));
|
|
18846
|
+
}
|
|
18603
18847
|
async function safetyCommand(command, target, options, stdin) {
|
|
18604
18848
|
let result;
|
|
18605
18849
|
try {
|
|
18606
|
-
const rules = buildSafetyRules(options);
|
|
18607
18850
|
const input3 = await inputFromTarget(target, options, stdin);
|
|
18608
18851
|
const read = await openTrace(input3, {
|
|
18609
18852
|
...options.format !== void 0 ? { format: options.format } : {}
|
|
18610
18853
|
});
|
|
18611
|
-
const
|
|
18612
|
-
|
|
18613
|
-
{
|
|
18614
|
-
rules,
|
|
18615
|
-
...options.run !== void 0 ? { runId: options.run } : {}
|
|
18616
|
-
}
|
|
18617
|
-
);
|
|
18618
|
-
const detectorFindings = checkResult.diagnostics.length === 0 ? redactionDetectorFindings(read, checkResult.runId) : [];
|
|
18619
|
-
result = resultFromParts({
|
|
18620
|
-
command,
|
|
18621
|
-
format: checkResult.format,
|
|
18622
|
-
runId: checkResult.runId,
|
|
18623
|
-
findings: [...checkResult.findings, ...detectorFindings],
|
|
18624
|
-
diagnostics: [
|
|
18625
|
-
...checkResult.diagnostics.map(diagnosticFromCheck),
|
|
18626
|
-
...warningDiagnostics(read.warnings, read.unsupportedFields)
|
|
18627
|
-
],
|
|
18628
|
-
warnings: read.warnings,
|
|
18629
|
-
unsupportedFields: read.unsupportedFields
|
|
18854
|
+
const source = assessOpenedTrace(read, {
|
|
18855
|
+
...options,
|
|
18856
|
+
...options.run !== void 0 ? { runId: options.run } : {}
|
|
18630
18857
|
});
|
|
18858
|
+
if (command === "scan") {
|
|
18859
|
+
result = { ...source, command: "scan" };
|
|
18860
|
+
} else {
|
|
18861
|
+
const profile = options.redactionProfile ?? "share";
|
|
18862
|
+
const rawContent = input3.type === "string" ? input3.content : input3.type === "file" ? await promises.readFile(input3.path, "utf-8") : void 0;
|
|
18863
|
+
if (rawContent === void 0) {
|
|
18864
|
+
result = {
|
|
18865
|
+
...source,
|
|
18866
|
+
command: "verify-safe",
|
|
18867
|
+
sourceAssessment: layerFromResult(source)
|
|
18868
|
+
};
|
|
18869
|
+
} else {
|
|
18870
|
+
const redacted = redactTraceContent(rawContent, profile);
|
|
18871
|
+
const artifactRead = await openTrace(
|
|
18872
|
+
{ type: "string", content: redacted.content },
|
|
18873
|
+
{ format: options.format ?? "agent-inspect-jsonl" }
|
|
18874
|
+
);
|
|
18875
|
+
const artifact = assessOpenedTrace(artifactRead, {
|
|
18876
|
+
...options,
|
|
18877
|
+
...options.run !== void 0 ? { runId: options.run } : {}
|
|
18878
|
+
});
|
|
18879
|
+
const detectors = [
|
|
18880
|
+
...new Set(redacted.findings.map((finding) => finding.detector))
|
|
18881
|
+
].sort((a, b) => a.localeCompare(b));
|
|
18882
|
+
result = resultFromParts({
|
|
18883
|
+
command: "verify-safe",
|
|
18884
|
+
format: artifact.format,
|
|
18885
|
+
runId: artifact.runId ?? source.runId,
|
|
18886
|
+
findings: artifact.findings,
|
|
18887
|
+
diagnostics: artifact.diagnostics,
|
|
18888
|
+
warnings: artifact.warnings,
|
|
18889
|
+
unsupportedFields: artifact.unsupportedFields,
|
|
18890
|
+
status: artifact.status,
|
|
18891
|
+
sourceAssessment: layerFromResult(source),
|
|
18892
|
+
artifactAssessment: layerFromResult(artifact),
|
|
18893
|
+
redactionSummary: {
|
|
18894
|
+
profile,
|
|
18895
|
+
findings: redacted.findings.length,
|
|
18896
|
+
detectors
|
|
18897
|
+
}
|
|
18898
|
+
});
|
|
18899
|
+
}
|
|
18900
|
+
}
|
|
18631
18901
|
} catch (error) {
|
|
18632
18902
|
const message = error instanceof Error ? error.message : String(error);
|
|
18633
18903
|
result = message.startsWith("--") ? invalidArgumentResult(command, error) : readErrorResult3(command, error);
|
|
18634
18904
|
}
|
|
18635
18905
|
process.exitCode = exitCodeFor3(result);
|
|
18636
|
-
if (options.json) printJson3(result);
|
|
18637
|
-
else printHuman3(result);
|
|
18906
|
+
if (options.json) printJson3(result, options.explain === true);
|
|
18907
|
+
else printHuman3(result, options.explain === true);
|
|
18638
18908
|
}
|
|
18639
18909
|
function scanCommand(target, options = {}, stdin = process.stdin) {
|
|
18640
18910
|
return safetyCommand("scan", target, options, stdin);
|
|
@@ -19116,15 +19386,8 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
19116
19386
|
process.exitCode = 1;
|
|
19117
19387
|
return;
|
|
19118
19388
|
}
|
|
19119
|
-
const
|
|
19120
|
-
const
|
|
19121
|
-
checkRuns.push({
|
|
19122
|
-
runId,
|
|
19123
|
-
status,
|
|
19124
|
-
errors: safety.summary.errors,
|
|
19125
|
-
warnings: safety.summary.warnings,
|
|
19126
|
-
findings: safety.summary.findings
|
|
19127
|
-
});
|
|
19389
|
+
const sourceSafety = assessOpenedTrace(read, { run: runId });
|
|
19390
|
+
const sourceStatus = safetyStatusFromAssess(sourceSafety.status);
|
|
19128
19391
|
const redacted = redactTraceContent(rawContent, toReportProfile(profile));
|
|
19129
19392
|
redactedJsonlByRun.set(runId, redacted.content);
|
|
19130
19393
|
combinedJsonl += redacted.content.endsWith("\n") ? redacted.content : `${redacted.content}
|
|
@@ -19137,6 +19400,34 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
19137
19400
|
findings: redacted.findings.length,
|
|
19138
19401
|
detectors
|
|
19139
19402
|
});
|
|
19403
|
+
let artifactSafety = sourceSafety;
|
|
19404
|
+
try {
|
|
19405
|
+
const artifactRead = await openTrace(
|
|
19406
|
+
{ type: "string", content: redacted.content },
|
|
19407
|
+
{ format: "agent-inspect-jsonl" }
|
|
19408
|
+
);
|
|
19409
|
+
artifactSafety = assessOpenedTrace(artifactRead, { run: runId });
|
|
19410
|
+
} catch {
|
|
19411
|
+
artifactSafety = {
|
|
19412
|
+
...sourceSafety,
|
|
19413
|
+
status: "UNKNOWN",
|
|
19414
|
+
ok: false,
|
|
19415
|
+
summary: {
|
|
19416
|
+
findings: sourceSafety.summary.findings,
|
|
19417
|
+
warnings: sourceSafety.summary.warnings,
|
|
19418
|
+
errors: Math.max(1, sourceSafety.summary.errors)
|
|
19419
|
+
}
|
|
19420
|
+
};
|
|
19421
|
+
}
|
|
19422
|
+
const status = safetyStatusFromAssess(artifactSafety.status);
|
|
19423
|
+
checkRuns.push({
|
|
19424
|
+
runId,
|
|
19425
|
+
status,
|
|
19426
|
+
sourceStatus,
|
|
19427
|
+
errors: artifactSafety.summary.errors,
|
|
19428
|
+
warnings: artifactSafety.summary.warnings,
|
|
19429
|
+
findings: artifactSafety.summary.findings
|
|
19430
|
+
});
|
|
19140
19431
|
const selected = read.runs.find((run) => run.runId === runId);
|
|
19141
19432
|
const runReport = exportRunTree(selected ?? read.runs[0], {
|
|
19142
19433
|
format: "html",
|
|
@@ -19165,13 +19456,13 @@ async function bundleCommand(runIdArg, options = {}) {
|
|
|
19165
19456
|
console.log(
|
|
19166
19457
|
writeJson4({
|
|
19167
19458
|
ok: false,
|
|
19168
|
-
error: `Bundle safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`,
|
|
19459
|
+
error: `Bundle artifact safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`,
|
|
19169
19460
|
checks: checks2
|
|
19170
19461
|
}).trimEnd()
|
|
19171
19462
|
);
|
|
19172
19463
|
} else {
|
|
19173
19464
|
console.error(
|
|
19174
|
-
`[AgentInspect] bundle refused: safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`
|
|
19465
|
+
`[AgentInspect] bundle refused: artifact safety status is ${checks2.aggregateStatus}. Pass --allow-unsafe to override.`
|
|
19175
19466
|
);
|
|
19176
19467
|
}
|
|
19177
19468
|
process.exitCode = 1;
|
|
@@ -21516,7 +21807,7 @@ function createCliProgram() {
|
|
|
21516
21807
|
"openinference-json",
|
|
21517
21808
|
"otlp-json"
|
|
21518
21809
|
])
|
|
21519
|
-
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
|
|
21810
|
+
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--explain", "explain each finding (path, confidence, redaction, override, bundle gate)").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
|
|
21520
21811
|
runCommand(() => scanCommand(target, opts));
|
|
21521
21812
|
});
|
|
21522
21813
|
program.command("verify-safe").description("Best-effort local trace safety verification").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").option("--dir <path>", "trace directory for run-id lookup").addOption(
|
|
@@ -21525,7 +21816,7 @@ function createCliProgram() {
|
|
|
21525
21816
|
"openinference-json",
|
|
21526
21817
|
"otlp-json"
|
|
21527
21818
|
])
|
|
21528
|
-
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
|
|
21819
|
+
).option("--run <run-id>", "select a run when the trace contains multiple runs").option("--json", "print deterministic JSON safety result").option("--explain", "explain each finding (path, confidence, redaction, override, bundle gate)").option("--max-string-length <number>", "unsafe threshold for string values").option("--max-array-length <number>", "unsafe threshold for array values").option("--max-object-keys <number>", "unsafe threshold for object key counts").option("--max-serialized-bytes <number>", "unsafe threshold for serialized values").action((target, opts) => {
|
|
21529
21820
|
runCommand(() => verifySafeCommand(target, opts));
|
|
21530
21821
|
});
|
|
21531
21822
|
program.command("artifacts").description("Create safe local CI trace artifacts").argument("<trace-path-or-run-id>", "trace file, directory, stdin -, or run id").requiredOption("--output-dir <path>", "directory for generated artifacts").option("--dir <path>", "trace directory for run-id lookup").addOption(
|