@theoremjs/react 0.3.4-next.574.g44edd0f → 0.3.4-next.590.g0246066
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/client/trace-story.d.ts +2 -0
- package/client/trace-story.js +22 -13
- package/package.json +3 -3
- package/ui/TraceGuardrails.js +11 -3
- package/ui/labels.d.ts +5 -0
- package/ui/labels.js +5 -0
package/client/trace-story.d.ts
CHANGED
|
@@ -92,6 +92,8 @@ export interface TraceGuardrailHit {
|
|
|
92
92
|
severityLabel: string;
|
|
93
93
|
/** The matched text, kept only when the profile records match previews. */
|
|
94
94
|
match?: string;
|
|
95
|
+
/** How many places the rule matched with this text. */
|
|
96
|
+
count: number;
|
|
95
97
|
}
|
|
96
98
|
/** One guardrail check: what it checked, what it did and why, and how long it took when timed. */
|
|
97
99
|
export interface TraceGuardrailCheck {
|
package/client/trace-story.js
CHANGED
|
@@ -232,24 +232,33 @@ function guardrailHits(value) {
|
|
|
232
232
|
if (!Array.isArray(value))
|
|
233
233
|
return [];
|
|
234
234
|
const fields = traceEventAttributeMeta('theorem.guardrail', 'hits')?.fields;
|
|
235
|
-
|
|
235
|
+
// why: a rule hits once per place it matched; with the text stripped the places read the same, so they count as one row.
|
|
236
|
+
const rows = new Map();
|
|
237
|
+
for (const hit of value.filter(isRecord)) {
|
|
236
238
|
if (typeof hit.rule !== 'string')
|
|
237
|
-
|
|
239
|
+
continue;
|
|
240
|
+
const match = typeof hit.match === 'string' ? hit.match : undefined;
|
|
241
|
+
const key = `${hit.rule}:${match ?? ''}`;
|
|
242
|
+
const seen = rows.get(key);
|
|
243
|
+
if (seen) {
|
|
244
|
+
seen.count += 1;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
238
247
|
const severity = typeof hit.severity === 'string' ? hit.severity : '';
|
|
239
248
|
// why: A host names its own rules on the hit; Theorem's are named in the catalog.
|
|
240
249
|
const rule = fields?.rule?.options?.[hit.rule];
|
|
241
250
|
const ruleDoc = typeof hit.doc === 'string' ? hit.doc : rule?.doc;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
251
|
+
rows.set(key, {
|
|
252
|
+
rule: hit.rule,
|
|
253
|
+
ruleLabel: typeof hit.label === 'string' ? hit.label : (rule?.label ?? hit.rule),
|
|
254
|
+
...(ruleDoc && { ruleDoc }),
|
|
255
|
+
severity,
|
|
256
|
+
severityLabel: fields?.severity?.options?.[severity]?.label ?? severity,
|
|
257
|
+
...(match !== undefined && { match }),
|
|
258
|
+
count: 1,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return [...rows.values()];
|
|
253
262
|
}
|
|
254
263
|
function eventOption(key, value) {
|
|
255
264
|
return traceEventAttributeMeta('theorem.guardrail', key)?.options?.[value];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theoremjs/react",
|
|
3
|
-
"version": "0.3.4-next.
|
|
3
|
+
"version": "0.3.4-next.590.g0246066",
|
|
4
4
|
"description": "React bindings for Theorem: headless hooks + transport, an Astryx chat UI, and a Web-standard server handler.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"@stylexjs/stylex": "0.19.0",
|
|
61
61
|
"react": "^19.0.0",
|
|
62
62
|
"react-dom": "^19.0.0",
|
|
63
|
-
"@theoremjs/agents": "^0.3.4-next.
|
|
64
|
-
"@theoremjs/web": "^0.3.4-next.
|
|
63
|
+
"@theoremjs/agents": "^0.3.4-next.590.g0246066",
|
|
64
|
+
"@theoremjs/web": "^0.3.4-next.590.g0246066"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
67
|
"@astryxdesign/core": {
|
package/ui/TraceGuardrails.js
CHANGED
|
@@ -14,8 +14,16 @@ function checkName(check) {
|
|
|
14
14
|
return check.checkLabel ?? check.stageLabel;
|
|
15
15
|
}
|
|
16
16
|
/** One rule a check matched: what it caught and how serious, why that matters, and the matched text when kept. The id is on hover, for finding the rule in code. */
|
|
17
|
-
function HitWhy({ hit,
|
|
18
|
-
|
|
17
|
+
function HitWhy({ hit, format }) {
|
|
18
|
+
const separator = format.t('@theorem.panel.trace.separator');
|
|
19
|
+
return (_jsxs(VStack, { gap: 0, children: [_jsx(HoverTip, { content: hit.rule, children: _jsx(Text, { type: "supporting", weight: "medium", children: [
|
|
20
|
+
hit.ruleLabel,
|
|
21
|
+
hit.severityLabel,
|
|
22
|
+
hit.count > 1 &&
|
|
23
|
+
format.t('@theorem.panel.trace.guardrails.places', { count: hit.count }),
|
|
24
|
+
]
|
|
25
|
+
.filter(Boolean)
|
|
26
|
+
.join(separator) }) }), hit.ruleDoc ? (_jsx(Text, { type: "supporting", color: "secondary", children: hit.ruleDoc })) : null, hit.match ? (_jsx(Text, { type: "supporting", color: "secondary", maxLines: 2, children: `“${hit.match}”` })) : null] }));
|
|
19
27
|
}
|
|
20
28
|
/** Why a check acted: which check it was and on what, each rule it matched, and the guardrail's own reason. */
|
|
21
29
|
function CheckWhy({ check, format }) {
|
|
@@ -26,7 +34,7 @@ function CheckWhy({ check, format }) {
|
|
|
26
34
|
check.tool && format.t('@theorem.panel.trace.guardrails.from', { tool: check.tool }),
|
|
27
35
|
]
|
|
28
36
|
.filter(Boolean)
|
|
29
|
-
.join(separator) }), check.hits.map((hit) => (_jsx(HitWhy, { hit: hit,
|
|
37
|
+
.join(separator) }), check.hits.map((hit) => (_jsx(HitWhy, { hit: hit, format: format }, `${hit.rule}:${hit.match ?? ''}`))), check.reason ? (_jsx(Text, { type: "supporting", color: "secondary", maxLines: 3, children: check.reason })) : null] }));
|
|
30
38
|
}
|
|
31
39
|
/** What a check did, as a token. A report of a call made after a remote read says so in place of "flagged". */
|
|
32
40
|
function ActionToken({ check, format }) {
|
package/ui/labels.d.ts
CHANGED
|
@@ -1115,6 +1115,11 @@ export declare const THEOREM_UI_CATALOG: {
|
|
|
1115
1115
|
readonly description: "Under the guardrail checks that acted: the checks that let the text through, each with what it checked and its time.";
|
|
1116
1116
|
readonly params: readonly ["checks"];
|
|
1117
1117
|
};
|
|
1118
|
+
readonly '@theorem.panel.trace.guardrails.places': {
|
|
1119
|
+
readonly defaultMessage: "{count, plural, one {# place} other {# places}}";
|
|
1120
|
+
readonly description: "A rule a guardrail check matched: how many places in the text it matched.";
|
|
1121
|
+
readonly params: readonly ["count"];
|
|
1122
|
+
};
|
|
1118
1123
|
readonly '@theorem.panel.trace.guardrails.from': {
|
|
1119
1124
|
readonly defaultMessage: "from {tool}";
|
|
1120
1125
|
readonly description: "A guardrail check: the tool the checked text came from.";
|
package/ui/labels.js
CHANGED
|
@@ -1063,6 +1063,11 @@ export const THEOREM_UI_CATALOG = {
|
|
|
1063
1063
|
description: 'Under the guardrail checks that acted: the checks that let the text through, each with what it checked and its time.',
|
|
1064
1064
|
params: ['checks'],
|
|
1065
1065
|
},
|
|
1066
|
+
'@theorem.panel.trace.guardrails.places': {
|
|
1067
|
+
defaultMessage: '{count, plural, one {# place} other {# places}}',
|
|
1068
|
+
description: 'A rule a guardrail check matched: how many places in the text it matched.',
|
|
1069
|
+
params: ['count'],
|
|
1070
|
+
},
|
|
1066
1071
|
'@theorem.panel.trace.guardrails.from': {
|
|
1067
1072
|
defaultMessage: 'from {tool}',
|
|
1068
1073
|
description: 'A guardrail check: the tool the checked text came from.',
|