@indemn/cli 1.0.0 → 1.1.1

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.
Files changed (106) hide show
  1. package/.claude-plugin/marketplace.json +28 -0
  2. package/README.md +29 -23
  3. package/dist/cli/agents.d.ts.map +1 -1
  4. package/dist/cli/agents.js +20 -0
  5. package/dist/cli/agents.js.map +1 -1
  6. package/dist/cli/eval.d.ts.map +1 -1
  7. package/dist/cli/eval.js +38 -0
  8. package/dist/cli/eval.js.map +1 -1
  9. package/dist/cli/index.js +1 -1
  10. package/dist/mcp/tools.d.ts.map +1 -1
  11. package/dist/mcp/tools.js +40 -0
  12. package/dist/mcp/tools.js.map +1 -1
  13. package/dist/report/agent-card/cover.d.ts +20 -0
  14. package/dist/report/agent-card/cover.d.ts.map +1 -0
  15. package/dist/report/agent-card/cover.js +58 -0
  16. package/dist/report/agent-card/cover.js.map +1 -0
  17. package/dist/report/agent-card/document.d.ts +7 -0
  18. package/dist/report/agent-card/document.d.ts.map +1 -0
  19. package/dist/report/agent-card/document.js +144 -0
  20. package/dist/report/agent-card/document.js.map +1 -0
  21. package/dist/report/agent-card/types.d.ts +77 -0
  22. package/dist/report/agent-card/types.d.ts.map +1 -0
  23. package/dist/report/agent-card/types.js +3 -0
  24. package/dist/report/agent-card/types.js.map +1 -0
  25. package/dist/report/assets/Indemn_PrimaryLogo_Iris.png +0 -0
  26. package/dist/report/assets/assets/Indemn_PrimaryLogo_Iris.png +0 -0
  27. package/dist/report/assets/assets/fonts/Barlow-Bold.ttf +0 -0
  28. package/dist/report/assets/assets/fonts/Barlow-Medium.ttf +0 -0
  29. package/dist/report/assets/assets/fonts/Barlow-Regular.ttf +0 -0
  30. package/dist/report/assets/assets/fonts/Barlow-SemiBold.ttf +0 -0
  31. package/dist/report/assets/fonts/Barlow-Bold.ttf +0 -0
  32. package/dist/report/assets/fonts/Barlow-Medium.ttf +0 -0
  33. package/dist/report/assets/fonts/Barlow-Regular.ttf +0 -0
  34. package/dist/report/assets/fonts/Barlow-SemiBold.ttf +0 -0
  35. package/dist/report/eval-report/cover.d.ts +16 -0
  36. package/dist/report/eval-report/cover.d.ts.map +1 -0
  37. package/dist/report/eval-report/cover.js +8 -0
  38. package/dist/report/eval-report/cover.js.map +1 -0
  39. package/dist/report/eval-report/document.d.ts +19 -0
  40. package/dist/report/eval-report/document.d.ts.map +1 -0
  41. package/dist/report/eval-report/document.js +32 -0
  42. package/dist/report/eval-report/document.js.map +1 -0
  43. package/dist/report/eval-report/example-page.d.ts +11 -0
  44. package/dist/report/eval-report/example-page.d.ts.map +1 -0
  45. package/dist/report/eval-report/example-page.js +18 -0
  46. package/dist/report/eval-report/example-page.js.map +1 -0
  47. package/dist/report/eval-report/item-page.d.ts +9 -0
  48. package/dist/report/eval-report/item-page.d.ts.map +1 -0
  49. package/dist/report/eval-report/item-page.js +72 -0
  50. package/dist/report/eval-report/item-page.js.map +1 -0
  51. package/dist/report/eval-report/matrix-page.d.ts +8 -0
  52. package/dist/report/eval-report/matrix-page.d.ts.map +1 -0
  53. package/dist/report/eval-report/matrix-page.js +31 -0
  54. package/dist/report/eval-report/matrix-page.js.map +1 -0
  55. package/dist/report/eval-report/types.d.ts +69 -0
  56. package/dist/report/eval-report/types.d.ts.map +1 -0
  57. package/dist/report/eval-report/types.js +7 -0
  58. package/dist/report/eval-report/types.js.map +1 -0
  59. package/dist/report/eval-report/v2-cover.d.ts +9 -0
  60. package/dist/report/eval-report/v2-cover.d.ts.map +1 -0
  61. package/dist/report/eval-report/v2-cover.js +32 -0
  62. package/dist/report/eval-report/v2-cover.js.map +1 -0
  63. package/dist/report/export.d.ts +17 -0
  64. package/dist/report/export.d.ts.map +1 -0
  65. package/dist/report/export.js +327 -0
  66. package/dist/report/export.js.map +1 -0
  67. package/dist/report/generate.d.ts +7 -0
  68. package/dist/report/generate.d.ts.map +1 -0
  69. package/dist/report/generate.js +30 -0
  70. package/dist/report/generate.js.map +1 -0
  71. package/dist/report/scoring.d.ts +90 -0
  72. package/dist/report/scoring.d.ts.map +1 -0
  73. package/dist/report/scoring.js +174 -0
  74. package/dist/report/scoring.js.map +1 -0
  75. package/dist/report/styles.d.ts +111 -0
  76. package/dist/report/styles.d.ts.map +1 -0
  77. package/dist/report/styles.js +134 -0
  78. package/dist/report/styles.js.map +1 -0
  79. package/docs/plans/2026-03-13-cli-exports.md +819 -0
  80. package/package.json +14 -4
  81. package/skills/eval-analysis/SKILL.md +178 -0
  82. package/skills/eval-analysis/references/data-shapes.md +279 -0
  83. package/src/cli/agents.ts +22 -0
  84. package/src/cli/eval.ts +42 -0
  85. package/src/cli/index.ts +1 -1
  86. package/src/mcp/tools.ts +50 -0
  87. package/src/report/agent-card/cover.tsx +232 -0
  88. package/src/report/agent-card/document.tsx +582 -0
  89. package/src/report/agent-card/types.ts +86 -0
  90. package/src/report/assets/Indemn_PrimaryLogo_Iris.png +0 -0
  91. package/src/report/assets/fonts/Barlow-Bold.ttf +0 -0
  92. package/src/report/assets/fonts/Barlow-Medium.ttf +0 -0
  93. package/src/report/assets/fonts/Barlow-Regular.ttf +0 -0
  94. package/src/report/assets/fonts/Barlow-SemiBold.ttf +0 -0
  95. package/src/report/eval-report/cover.tsx +69 -0
  96. package/src/report/eval-report/document.tsx +105 -0
  97. package/src/report/eval-report/example-page.tsx +121 -0
  98. package/src/report/eval-report/item-page.tsx +211 -0
  99. package/src/report/eval-report/matrix-page.tsx +124 -0
  100. package/src/report/eval-report/types.ts +77 -0
  101. package/src/report/eval-report/v2-cover.tsx +102 -0
  102. package/src/report/export.ts +394 -0
  103. package/src/report/generate.tsx +37 -0
  104. package/src/report/scoring.ts +281 -0
  105. package/src/report/styles.ts +138 -0
  106. package/tsconfig.json +2 -1
@@ -0,0 +1,582 @@
1
+ // Agent card document — ported from AgentCardDocument.tsx
2
+ import { Document, Page, View, Text } from '@react-pdf/renderer';
3
+ import { AgentCardCover } from './cover.js';
4
+ import { styles, colors } from '../styles.js';
5
+ import { getScoreHexColor } from '../scoring.js';
6
+ import type {
7
+ AgentCardData,
8
+ AgentCardTool,
9
+ AgentCardKnowledgeBase,
10
+ AgentCardLLMConfig,
11
+ AgentCardEvalRun,
12
+ LifecycleStage,
13
+ V1Owner,
14
+ ComponentScoreDetail,
15
+ } from './types.js';
16
+
17
+ interface AgentCardDocumentProps {
18
+ data: AgentCardData;
19
+ }
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Shared helpers
23
+ // ---------------------------------------------------------------------------
24
+
25
+ function PageHeader({ title, pageNumber }: { title: string; pageNumber: number }) {
26
+ return (
27
+ <View style={styles.pageHeader}>
28
+ <Text style={styles.pageTitle}>{title}</Text>
29
+ <Text style={styles.pageNumber}>Page {pageNumber}</Text>
30
+ </View>
31
+ );
32
+ }
33
+
34
+ function PageFooter({ generatedDate }: { generatedDate: string }) {
35
+ return (
36
+ <View style={styles.footer}>
37
+ <Text style={styles.caption}>Generated {generatedDate}</Text>
38
+ <Text style={styles.caption}>Indemn Agent Card</Text>
39
+ </View>
40
+ );
41
+ }
42
+
43
+ function ComponentScoreBadge({ label, detail }: { label: string; detail?: ComponentScoreDetail }) {
44
+ if (!detail || detail.total === 0) return null;
45
+ const rate = detail.passed / detail.total;
46
+ const pct = Math.round(rate * 100);
47
+ return (
48
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 6 }}>
49
+ <View
50
+ style={{
51
+ backgroundColor: `${getScoreHexColor(rate)}18`,
52
+ paddingHorizontal: 8,
53
+ paddingVertical: 3,
54
+ borderRadius: 4,
55
+ }}
56
+ >
57
+ <Text style={{ fontSize: 9, fontWeight: 600, color: getScoreHexColor(rate) }}>
58
+ {label}: {pct}% ({detail.passed}/{detail.total})
59
+ </Text>
60
+ </View>
61
+ </View>
62
+ );
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Page 2: System Prompt
67
+ // ---------------------------------------------------------------------------
68
+
69
+ function SystemPromptPage({
70
+ systemPrompt,
71
+ generatedDate,
72
+ componentScore,
73
+ }: {
74
+ systemPrompt?: string;
75
+ generatedDate: string;
76
+ componentScore?: ComponentScoreDetail;
77
+ }) {
78
+ return (
79
+ <Page size="A4" style={styles.page}>
80
+ <View style={styles.pageHeader}>
81
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
82
+ <Text style={styles.pageTitle}>System Prompt</Text>
83
+ <ComponentScoreBadge label="Prompt Score" detail={componentScore} />
84
+ </View>
85
+ <Text style={styles.pageNumber}>Page 2</Text>
86
+ </View>
87
+
88
+ {systemPrompt ? (
89
+ <View
90
+ style={{
91
+ backgroundColor: colors.background,
92
+ borderRadius: 6,
93
+ padding: 16,
94
+ flex: 1,
95
+ marginBottom: 60,
96
+ }}
97
+ >
98
+ <Text
99
+ style={{
100
+ fontFamily: 'Courier',
101
+ fontSize: 9,
102
+ lineHeight: 1.6,
103
+ color: colors.eggplant,
104
+ }}
105
+ >
106
+ {systemPrompt}
107
+ </Text>
108
+ </View>
109
+ ) : (
110
+ <View style={[styles.card, { alignItems: 'center', paddingVertical: 40 }]}>
111
+ <Text style={{ fontSize: 12, color: colors.muted }}>
112
+ No system prompt configured
113
+ </Text>
114
+ </View>
115
+ )}
116
+
117
+ <PageFooter generatedDate={generatedDate} />
118
+ </Page>
119
+ );
120
+ }
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Page 3: Tools
124
+ // ---------------------------------------------------------------------------
125
+
126
+ function ToolsPage({
127
+ tools,
128
+ generatedDate,
129
+ componentScore,
130
+ }: {
131
+ tools: AgentCardTool[];
132
+ generatedDate: string;
133
+ componentScore?: ComponentScoreDetail;
134
+ }) {
135
+ return (
136
+ <Page size="A4" style={styles.page}>
137
+ <View style={styles.pageHeader}>
138
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
139
+ <Text style={styles.pageTitle}>Tools ({tools.length})</Text>
140
+ <ComponentScoreBadge label="Function Score" detail={componentScore} />
141
+ </View>
142
+ <Text style={styles.pageNumber}>Page 3</Text>
143
+ </View>
144
+
145
+ {tools.length > 0 ? (
146
+ <View
147
+ style={{
148
+ flexDirection: 'row',
149
+ flexWrap: 'wrap',
150
+ gap: 12,
151
+ marginBottom: 60,
152
+ }}
153
+ >
154
+ {tools.map((tool) => (
155
+ <View
156
+ key={tool.id}
157
+ style={{
158
+ width: '48%',
159
+ backgroundColor: colors.background,
160
+ borderRadius: 6,
161
+ padding: 12,
162
+ }}
163
+ >
164
+ <Text style={{ fontSize: 11, fontWeight: 600, color: colors.eggplant, marginBottom: 4 }}>
165
+ {tool.name}
166
+ </Text>
167
+ <Text style={{ fontSize: 9, fontFamily: 'Courier', color: colors.muted, marginBottom: 4 }}>
168
+ {tool.tool_name}
169
+ </Text>
170
+ {tool.description && (
171
+ <Text style={{ fontSize: 9, color: colors.muted, lineHeight: 1.4, marginBottom: 4 }}>
172
+ {tool.description}
173
+ </Text>
174
+ )}
175
+ <View
176
+ style={{
177
+ backgroundColor: colors.white,
178
+ paddingHorizontal: 6,
179
+ paddingVertical: 2,
180
+ borderRadius: 3,
181
+ alignSelf: 'flex-start',
182
+ }}
183
+ >
184
+ <Text style={{ fontSize: 8, fontWeight: 500, color: colors.iris }}>
185
+ {tool.type}
186
+ </Text>
187
+ </View>
188
+ </View>
189
+ ))}
190
+ </View>
191
+ ) : (
192
+ <View style={[styles.card, { alignItems: 'center', paddingVertical: 40 }]}>
193
+ <Text style={{ fontSize: 12, color: colors.muted }}>
194
+ No tools configured
195
+ </Text>
196
+ </View>
197
+ )}
198
+
199
+ <PageFooter generatedDate={generatedDate} />
200
+ </Page>
201
+ );
202
+ }
203
+
204
+ // ---------------------------------------------------------------------------
205
+ // Page 4: Knowledge Bases
206
+ // ---------------------------------------------------------------------------
207
+
208
+ function KnowledgeBasesPage({
209
+ knowledgeBases,
210
+ generatedDate,
211
+ componentScore,
212
+ }: {
213
+ knowledgeBases: AgentCardKnowledgeBase[];
214
+ generatedDate: string;
215
+ componentScore?: ComponentScoreDetail;
216
+ }) {
217
+ return (
218
+ <Page size="A4" style={styles.page}>
219
+ <View style={styles.pageHeader}>
220
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
221
+ <Text style={styles.pageTitle}>Knowledge Bases ({knowledgeBases.length})</Text>
222
+ <ComponentScoreBadge label="KB Score" detail={componentScore} />
223
+ </View>
224
+ <Text style={styles.pageNumber}>Page 4</Text>
225
+ </View>
226
+
227
+ {knowledgeBases.length > 0 ? (
228
+ <View
229
+ style={{
230
+ flexDirection: 'row',
231
+ flexWrap: 'wrap',
232
+ gap: 12,
233
+ marginBottom: 60,
234
+ }}
235
+ >
236
+ {knowledgeBases.map((kb) => (
237
+ <View
238
+ key={kb.id}
239
+ style={{
240
+ width: '48%',
241
+ backgroundColor: colors.background,
242
+ borderRadius: 6,
243
+ padding: 12,
244
+ }}
245
+ >
246
+ <Text style={{ fontSize: 11, fontWeight: 600, color: colors.eggplant, marginBottom: 4 }}>
247
+ {kb.name}
248
+ </Text>
249
+ <View style={{ flexDirection: 'row', gap: 8, alignItems: 'center' }}>
250
+ <View
251
+ style={{
252
+ backgroundColor: colors.white,
253
+ paddingHorizontal: 6,
254
+ paddingVertical: 2,
255
+ borderRadius: 3,
256
+ }}
257
+ >
258
+ <Text style={{ fontSize: 8, fontWeight: 500, color: colors.iris }}>
259
+ {kb.type}
260
+ </Text>
261
+ </View>
262
+ <Text style={{ fontSize: 9, color: colors.muted }}>
263
+ {kb.document_count} documents
264
+ </Text>
265
+ </View>
266
+ </View>
267
+ ))}
268
+ </View>
269
+ ) : (
270
+ <View style={[styles.card, { alignItems: 'center', paddingVertical: 40 }]}>
271
+ <Text style={{ fontSize: 12, color: colors.muted }}>
272
+ No knowledge bases linked
273
+ </Text>
274
+ </View>
275
+ )}
276
+
277
+ <PageFooter generatedDate={generatedDate} />
278
+ </Page>
279
+ );
280
+ }
281
+
282
+ // ---------------------------------------------------------------------------
283
+ // Page 5: LLM Configuration
284
+ // ---------------------------------------------------------------------------
285
+
286
+ function LLMConfigPage({
287
+ llmConfig,
288
+ generatedDate,
289
+ }: {
290
+ llmConfig?: AgentCardLLMConfig;
291
+ generatedDate: string;
292
+ }) {
293
+ return (
294
+ <Page size="A4" style={styles.page}>
295
+ <PageHeader title="LLM Configuration" pageNumber={5} />
296
+
297
+ {llmConfig ? (
298
+ <View style={[styles.card, { marginBottom: 60 }]}>
299
+ <View style={{ flexDirection: 'row', marginBottom: 12 }}>
300
+ <View style={{ flex: 1 }}>
301
+ <Text style={{ fontSize: 10, color: colors.muted, marginBottom: 4 }}>Provider</Text>
302
+ <Text style={{ fontSize: 12, fontWeight: 500, color: colors.eggplant }}>
303
+ {llmConfig.provider || 'N/A'}
304
+ </Text>
305
+ </View>
306
+ <View style={{ flex: 1 }}>
307
+ <Text style={{ fontSize: 10, color: colors.muted, marginBottom: 4 }}>Model</Text>
308
+ <Text style={{ fontSize: 12, fontWeight: 500, fontFamily: 'Courier', color: colors.eggplant }}>
309
+ {llmConfig.model || 'N/A'}
310
+ </Text>
311
+ </View>
312
+ </View>
313
+
314
+ {llmConfig.parameters && Object.keys(llmConfig.parameters).length > 0 && (
315
+ <View>
316
+ <Text style={{ fontSize: 10, color: colors.muted, marginBottom: 8 }}>Parameters</Text>
317
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 12 }}>
318
+ {Object.entries(llmConfig.parameters).map(([key, value]) => (
319
+ <View key={key} style={{ backgroundColor: colors.white, borderRadius: 4, padding: 8, minWidth: 100 }}>
320
+ <Text style={{ fontSize: 9, color: colors.muted, marginBottom: 2 }}>
321
+ {key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())}
322
+ </Text>
323
+ <Text style={{ fontSize: 11, fontFamily: 'Courier', color: colors.eggplant }}>
324
+ {String(value)}
325
+ </Text>
326
+ </View>
327
+ ))}
328
+ </View>
329
+ </View>
330
+ )}
331
+ </View>
332
+ ) : (
333
+ <View style={[styles.card, { alignItems: 'center', paddingVertical: 40 }]}>
334
+ <Text style={{ fontSize: 12, color: colors.muted }}>
335
+ No LLM configuration set (using defaults)
336
+ </Text>
337
+ </View>
338
+ )}
339
+
340
+ <PageFooter generatedDate={generatedDate} />
341
+ </Page>
342
+ );
343
+ }
344
+
345
+ // ---------------------------------------------------------------------------
346
+ // Page 6: Evaluation History
347
+ // ---------------------------------------------------------------------------
348
+
349
+ function EvaluationHistoryPage({
350
+ runs,
351
+ generatedDate,
352
+ }: {
353
+ runs: AgentCardEvalRun[];
354
+ generatedDate: string;
355
+ }) {
356
+ const completedRuns = runs.filter(r => r.status === 'completed');
357
+ const recentRuns = completedRuns.slice(0, 10);
358
+
359
+ return (
360
+ <Page size="A4" style={styles.page}>
361
+ <PageHeader title={`Evaluation History (${completedRuns.length} runs)`} pageNumber={6} />
362
+
363
+ {recentRuns.length > 0 ? (
364
+ <View style={{ marginBottom: 60 }}>
365
+ {/* Table header */}
366
+ <View
367
+ style={{
368
+ flexDirection: 'row',
369
+ backgroundColor: colors.eggplant,
370
+ borderTopLeftRadius: 6,
371
+ borderTopRightRadius: 6,
372
+ paddingVertical: 8,
373
+ paddingHorizontal: 12,
374
+ }}
375
+ >
376
+ <Text style={{ flex: 2, fontSize: 9, fontWeight: 600, color: colors.white }}>Date</Text>
377
+ <Text style={{ flex: 2, fontSize: 9, fontWeight: 600, color: colors.white }}>Model</Text>
378
+ <Text style={{ flex: 1, fontSize: 9, fontWeight: 600, color: colors.white, textAlign: 'center' }}>Items</Text>
379
+ <Text style={{ flex: 1.5, fontSize: 9, fontWeight: 600, color: colors.white, textAlign: 'center' }}>Criteria</Text>
380
+ <Text style={{ flex: 1.5, fontSize: 9, fontWeight: 600, color: colors.white, textAlign: 'center' }}>Rubric</Text>
381
+ </View>
382
+
383
+ {/* Table rows */}
384
+ {recentRuns.map((run, i) => {
385
+ const date = run.completed_at || run.created_at;
386
+ const dateStr = new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
387
+ const model = run.bot_llm_model || 'N/A';
388
+ const criteriaStr = run.criteriaScore !== null ? `${run.criteriaScore}% (${run.criteriaPassed}/${run.criteriaTotal})` : '-';
389
+ const rubricStr = run.rubricScore !== null ? `${run.rubricScore}% (${run.rubricRulesPassed}/${run.rubricRulesTotal})` : '-';
390
+
391
+ return (
392
+ <View
393
+ key={run.run_id}
394
+ style={{
395
+ flexDirection: 'row',
396
+ backgroundColor: i % 2 === 0 ? colors.background : colors.white,
397
+ paddingVertical: 7,
398
+ paddingHorizontal: 12,
399
+ ...(i === recentRuns.length - 1 ? { borderBottomLeftRadius: 6, borderBottomRightRadius: 6 } : {}),
400
+ }}
401
+ >
402
+ <Text style={{ flex: 2, fontSize: 9, color: colors.eggplant }}>{dateStr}</Text>
403
+ <Text style={{ flex: 2, fontSize: 9, fontFamily: 'Courier', color: colors.eggplant }}>{model}</Text>
404
+ <Text style={{ flex: 1, fontSize: 9, color: colors.muted, textAlign: 'center' }}>{run.completed}/{run.total}</Text>
405
+ <Text style={{ flex: 1.5, fontSize: 9, fontWeight: 500, color: run.criteriaScore !== null ? getScoreHexColor(run.criteriaScore / 100) : colors.muted, textAlign: 'center' }}>
406
+ {criteriaStr}
407
+ </Text>
408
+ <Text style={{ flex: 1.5, fontSize: 9, fontWeight: 500, color: run.rubricScore !== null ? getScoreHexColor(run.rubricScore / 100) : colors.muted, textAlign: 'center' }}>
409
+ {rubricStr}
410
+ </Text>
411
+ </View>
412
+ );
413
+ })}
414
+ </View>
415
+ ) : (
416
+ <View style={[styles.card, { alignItems: 'center', paddingVertical: 40 }]}>
417
+ <Text style={{ fontSize: 12, color: colors.muted }}>
418
+ No completed evaluation runs
419
+ </Text>
420
+ </View>
421
+ )}
422
+
423
+ <PageFooter generatedDate={generatedDate} />
424
+ </Page>
425
+ );
426
+ }
427
+
428
+ // ---------------------------------------------------------------------------
429
+ // Page 7: Metadata
430
+ // ---------------------------------------------------------------------------
431
+
432
+ function MetadataPage({
433
+ botId,
434
+ organizationId,
435
+ lifecycle,
436
+ owner,
437
+ generatedDate,
438
+ }: {
439
+ botId: string;
440
+ organizationId?: string;
441
+ lifecycle?: LifecycleStage;
442
+ owner?: V1Owner;
443
+ generatedDate: string;
444
+ }) {
445
+ const ownerName = owner
446
+ ? [owner.firstname, owner.lastname].filter(Boolean).join(' ') || owner.email || 'Unknown'
447
+ : undefined;
448
+
449
+ const lcColors: Record<string, { bg: string; text: string }> = {
450
+ prototype: { bg: '#e5e7eb', text: '#4b5563' },
451
+ beta: { bg: '#fef3c7', text: '#92400e' },
452
+ published: { bg: '#d1fae5', text: '#065f46' },
453
+ };
454
+ const lc = lifecycle ? lcColors[lifecycle] || lcColors.prototype : null;
455
+
456
+ return (
457
+ <Page size="A4" style={styles.page}>
458
+ <PageHeader title="Metadata" pageNumber={7} />
459
+
460
+ <View style={[styles.card, { marginBottom: 60 }]}>
461
+ {/* Row 1: Lifecycle + Owner */}
462
+ <View style={{ flexDirection: 'row', marginBottom: 16 }}>
463
+ <View style={{ flex: 1 }}>
464
+ <Text style={{ fontSize: 10, color: colors.muted, marginBottom: 4 }}>Lifecycle Stage</Text>
465
+ {lc ? (
466
+ <View style={{ backgroundColor: lc.bg, paddingHorizontal: 8, paddingVertical: 3, borderRadius: 4, alignSelf: 'flex-start' }}>
467
+ <Text style={{ fontSize: 10, fontWeight: 600, color: lc.text }}>
468
+ {lifecycle!.charAt(0).toUpperCase() + lifecycle!.slice(1)}
469
+ </Text>
470
+ </View>
471
+ ) : (
472
+ <Text style={{ fontSize: 11, color: colors.muted }}>Not set</Text>
473
+ )}
474
+ </View>
475
+ <View style={{ flex: 1 }}>
476
+ <Text style={{ fontSize: 10, color: colors.muted, marginBottom: 4 }}>Owner</Text>
477
+ <Text style={{ fontSize: 11, color: colors.eggplant }}>
478
+ {ownerName || 'Unassigned'}
479
+ </Text>
480
+ {owner?.email && ownerName !== owner.email && (
481
+ <Text style={{ fontSize: 9, color: colors.muted }}>{owner.email}</Text>
482
+ )}
483
+ </View>
484
+ </View>
485
+
486
+ {/* Row 2: Bot ID + Org ID */}
487
+ <View style={{ flexDirection: 'row', marginBottom: 12 }}>
488
+ <View style={{ flex: 1 }}>
489
+ <Text style={{ fontSize: 10, color: colors.muted, marginBottom: 4 }}>Bot ID</Text>
490
+ <Text style={{ fontSize: 11, fontFamily: 'Courier', color: colors.eggplant }}>
491
+ {botId}
492
+ </Text>
493
+ </View>
494
+ <View style={{ flex: 1 }}>
495
+ <Text style={{ fontSize: 10, color: colors.muted, marginBottom: 4 }}>Organization ID</Text>
496
+ <Text style={{ fontSize: 11, fontFamily: 'Courier', color: colors.eggplant }}>
497
+ {organizationId || 'N/A'}
498
+ </Text>
499
+ </View>
500
+ </View>
501
+
502
+ {/* Row 3: Generated date */}
503
+ <View style={{ flexDirection: 'row' }}>
504
+ <View style={{ flex: 1 }}>
505
+ <Text style={{ fontSize: 10, color: colors.muted, marginBottom: 4 }}>Report Generated</Text>
506
+ <Text style={{ fontSize: 11, color: colors.eggplant }}>{generatedDate}</Text>
507
+ </View>
508
+ </View>
509
+ </View>
510
+
511
+ <PageFooter generatedDate={generatedDate} />
512
+ </Page>
513
+ );
514
+ }
515
+
516
+ // ---------------------------------------------------------------------------
517
+ // Document
518
+ // ---------------------------------------------------------------------------
519
+
520
+ export function AgentCardDocument({ data }: AgentCardDocumentProps) {
521
+ const hasEvalRuns = data.evaluationRuns && data.evaluationRuns.length > 0;
522
+
523
+ return (
524
+ <Document
525
+ title={`Indemn Agent Card - ${data.name}`}
526
+ author="Indemn Platform"
527
+ subject={`Agent card for ${data.name}`}
528
+ >
529
+ {/* Page 1: Cover */}
530
+ <AgentCardCover
531
+ agentName={data.name}
532
+ description={data.description}
533
+ channels={data.channels}
534
+ generatedDate={data.generatedDate}
535
+ lifecycle={data.lifecycle}
536
+ owner={data.owner}
537
+ evaluationSummary={data.evaluationSummary}
538
+ />
539
+
540
+ {/* Page 2: System Prompt */}
541
+ <SystemPromptPage
542
+ systemPrompt={data.system_prompt}
543
+ generatedDate={data.generatedDate}
544
+ componentScore={data.componentScores?.prompt}
545
+ />
546
+
547
+ {/* Page 3: Tools */}
548
+ <ToolsPage
549
+ tools={data.tools}
550
+ generatedDate={data.generatedDate}
551
+ componentScore={data.componentScores?.function}
552
+ />
553
+
554
+ {/* Page 4: Knowledge Bases */}
555
+ <KnowledgeBasesPage
556
+ knowledgeBases={data.knowledge_bases}
557
+ generatedDate={data.generatedDate}
558
+ componentScore={data.componentScores?.knowledge_base}
559
+ />
560
+
561
+ {/* Page 5: LLM Configuration */}
562
+ <LLMConfigPage llmConfig={data.llm_config} generatedDate={data.generatedDate} />
563
+
564
+ {/* Page 6: Evaluation History (only if runs exist) */}
565
+ {hasEvalRuns && (
566
+ <EvaluationHistoryPage
567
+ runs={data.evaluationRuns!}
568
+ generatedDate={data.generatedDate}
569
+ />
570
+ )}
571
+
572
+ {/* Page 7 (or 6 if no eval history): Metadata */}
573
+ <MetadataPage
574
+ botId={data.id}
575
+ organizationId={data.organization_id}
576
+ lifecycle={data.lifecycle}
577
+ owner={data.owner}
578
+ generatedDate={data.generatedDate}
579
+ />
580
+ </Document>
581
+ );
582
+ }
@@ -0,0 +1,86 @@
1
+ // Agent card data types
2
+
3
+ export type LifecycleStage = 'prototype' | 'beta' | 'published';
4
+
5
+ export interface V1Owner {
6
+ firstname?: string;
7
+ lastname?: string;
8
+ email?: string;
9
+ }
10
+
11
+ export interface ComponentScoreDetail {
12
+ score?: number;
13
+ total: number;
14
+ passed: number;
15
+ }
16
+
17
+ export interface ComponentScores {
18
+ prompt?: ComponentScoreDetail;
19
+ knowledge_base?: ComponentScoreDetail;
20
+ function?: ComponentScoreDetail;
21
+ general?: ComponentScoreDetail;
22
+ }
23
+
24
+ export interface AgentCardTool {
25
+ id: string;
26
+ name: string;
27
+ tool_name: string;
28
+ type: string;
29
+ description?: string;
30
+ }
31
+
32
+ export interface AgentCardKnowledgeBase {
33
+ id: string;
34
+ name: string;
35
+ type: string;
36
+ document_count: number;
37
+ }
38
+
39
+ export interface AgentCardLLMConfig {
40
+ provider?: string;
41
+ model?: string;
42
+ parameters?: Record<string, unknown>;
43
+ }
44
+
45
+ export interface AgentCardEvalRun {
46
+ run_id: string;
47
+ status: string;
48
+ created_at: string;
49
+ completed_at?: string;
50
+ bot_llm_model?: string;
51
+ bot_llm_provider?: string;
52
+ criteriaScore: number | null; // 0-100
53
+ rubricScore: number | null; // 0-100
54
+ criteriaPassed: number;
55
+ criteriaTotal: number;
56
+ rubricRulesPassed: number;
57
+ rubricRulesTotal: number;
58
+ total: number;
59
+ completed: number;
60
+ }
61
+
62
+ export interface AgentCardData {
63
+ id: string;
64
+ name: string;
65
+ description?: string;
66
+ channels: string[];
67
+ organization_id?: string;
68
+ system_prompt?: string;
69
+ tools: AgentCardTool[];
70
+ knowledge_bases: AgentCardKnowledgeBase[];
71
+ llm_config?: AgentCardLLMConfig;
72
+ generatedDate: string;
73
+ lifecycle?: LifecycleStage;
74
+ owner?: V1Owner;
75
+ evaluationSummary?: {
76
+ criteriaRate: number | null;
77
+ rubricRate: number | null;
78
+ criteriaPassed: number;
79
+ criteriaTotal: number;
80
+ rubricRulesPassed: number;
81
+ rubricRulesTotal: number;
82
+ totalRuns: number;
83
+ };
84
+ componentScores?: ComponentScores;
85
+ evaluationRuns?: AgentCardEvalRun[];
86
+ }