@theoremjs/react 0.3.4-next.570.gfda95a3 → 0.3.4-next.586.ga59d695

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.
@@ -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 {
@@ -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
- return value.filter(isRecord).flatMap((hit) => {
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
- return [];
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
- return [
243
- {
244
- rule: hit.rule,
245
- ruleLabel: typeof hit.label === 'string' ? hit.label : (rule?.label ?? hit.rule),
246
- ...(ruleDoc && { ruleDoc }),
247
- severity,
248
- severityLabel: fields?.severity?.options?.[severity]?.label ?? severity,
249
- ...(typeof hit.match === 'string' && { match: hit.match }),
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.570.gfda95a3",
3
+ "version": "0.3.4-next.586.ga59d695",
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.570.gfda95a3",
64
- "@theoremjs/web": "^0.3.4-next.570.gfda95a3"
63
+ "@theoremjs/agents": "^0.3.4-next.586.ga59d695",
64
+ "@theoremjs/web": "^0.3.4-next.586.ga59d695"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "@astryxdesign/core": {
@@ -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, separator }) {
18
- return (_jsxs(VStack, { gap: 0, children: [_jsx(HoverTip, { content: hit.rule, children: _jsx(Text, { type: "supporting", weight: "medium", children: [hit.ruleLabel, hit.severityLabel].filter(Boolean).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] }));
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, separator: separator }, `${hit.rule}:${hit.match ?? ''}`))), check.reason ? (_jsx(Text, { type: "supporting", color: "secondary", maxLines: 3, children: check.reason })) : null] }));
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.',