@shield-agent/kya 0.32.4 → 0.32.5
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/.cursor-plugin/plugin.json +2 -2
- package/.mcp.json +1 -1
- package/README.md +49 -49
- package/amp/settings.example.json +1 -1
- package/catalog/agent-trust-baseline-v0.json +75 -75
- package/claude/claude_desktop_config.example.json +1 -1
- package/cline/cline_mcp_settings.example.json +1 -1
- package/copilot/mcp-config.example.json +1 -1
- package/dist/certify/evaluate.js +23 -23
- package/dist/certify/evaluate.js.map +1 -1
- package/dist/commands/orr.d.ts.map +1 -1
- package/dist/commands/orr.js +43 -43
- package/dist/commands/orr.js.map +1 -1
- package/dist/receipt/live-server.js +1 -1
- package/dist/receipt/live-server.js.map +1 -1
- package/dist/receipt/render-receipt.d.ts.map +1 -1
- package/dist/receipt/render-receipt.js +61 -49
- package/dist/receipt/render-receipt.js.map +1 -1
- package/droid/README.md +1 -1
- package/droid/mcp.example.json +1 -1
- package/gemini/settings.example.json +1 -1
- package/grok/README.md +1 -1
- package/kilo/kilo.example.json +1 -1
- package/kimi/mcp.example.json +1 -1
- package/kiro/mcp.example.json +1 -1
- package/manifest.json +1 -1
- package/mastracode/mcp.example.json +1 -1
- package/mcp.json +1 -1
- package/openai/codex.config.example.toml +1 -1
- package/opencode/opencode.example.json +1 -1
- package/package.json +2 -2
- package/qwen/settings.example.json +1 -1
- package/server.json +2 -2
|
@@ -579,7 +579,7 @@ function reasonsPanel(agg) {
|
|
|
579
579
|
.map((r) => `<span class="chip"><code>${esc(clip(r.label, 60))}</code> × ${r.count}</span>`)
|
|
580
580
|
.join("\n ");
|
|
581
581
|
return `<section class="panel" aria-label="Reasons">
|
|
582
|
-
<h2>
|
|
582
|
+
<h2>Why calls were allowed, held, or blocked</h2>
|
|
583
583
|
<div class="chiprow">
|
|
584
584
|
${chips}
|
|
585
585
|
</div>
|
|
@@ -689,7 +689,7 @@ function wiredHostsPanel(hosts) {
|
|
|
689
689
|
})
|
|
690
690
|
.join("\n ");
|
|
691
691
|
return `<section class="panel" aria-label="Wired hosts">
|
|
692
|
-
<h2>
|
|
692
|
+
<h2>Connected tools</h2>
|
|
693
693
|
<ul class="rows">
|
|
694
694
|
${rows}
|
|
695
695
|
</ul>
|
|
@@ -774,7 +774,7 @@ function capabilitiesPanel(model) {
|
|
|
774
774
|
},
|
|
775
775
|
{
|
|
776
776
|
name: "ORR board",
|
|
777
|
-
blurb: "
|
|
777
|
+
blurb: "Checks if your agent setup is safe to run",
|
|
778
778
|
live: !!model.orr,
|
|
779
779
|
liveLabel: "ran",
|
|
780
780
|
setup: "runs automatically at <code>kya start</code>; refresh with <code>kya orr run</code>",
|
|
@@ -1273,20 +1273,20 @@ function orrPanel(orr, nowMs, live) {
|
|
|
1273
1273
|
: "";
|
|
1274
1274
|
if (!orr) {
|
|
1275
1275
|
return `<section class="panel" aria-label="ORR">
|
|
1276
|
-
<h2>
|
|
1277
|
-
<p class="line">No
|
|
1276
|
+
<h2>Readiness check</h2>
|
|
1277
|
+
<p class="line">No readiness board yet. kya runs one when the report starts and refreshes it every 30 minutes. The board checks if your agent setup is safe to run.</p>
|
|
1278
1278
|
<p class="mute small">CLI: <code>kya orr run --path .</code></p>
|
|
1279
1279
|
${run}
|
|
1280
1280
|
</section>`;
|
|
1281
1281
|
}
|
|
1282
1282
|
const target = orr.targetName
|
|
1283
|
-
? `<span class="mute">
|
|
1283
|
+
? `<span class="mute">checked ${esc(clip(orr.targetName, 60))}</span>`
|
|
1284
1284
|
: "";
|
|
1285
1285
|
const failure = orr.primaryFailureMode
|
|
1286
|
-
? `<p class="line">
|
|
1286
|
+
? `<p class="line">Main problem: ${esc(clip(orr.primaryFailureMode, 200))}</p>`
|
|
1287
1287
|
: "";
|
|
1288
1288
|
const fix = orr.mostUrgentFix
|
|
1289
|
-
? `<p class="line">
|
|
1289
|
+
? `<p class="line">First fix to make: ${esc(clip(orr.mostUrgentFix, 200))}</p>`
|
|
1290
1290
|
: "";
|
|
1291
1291
|
const when = orr.generatedAt ? ` · ${esc(relativeTime(orr.generatedAt, nowMs))}` : "";
|
|
1292
1292
|
const findings = orr.findings.length
|
|
@@ -1303,19 +1303,31 @@ function orrPanel(orr, nowMs, live) {
|
|
|
1303
1303
|
.join("")}</ul>`
|
|
1304
1304
|
: "";
|
|
1305
1305
|
return `<section class="panel" aria-label="ORR">
|
|
1306
|
-
<h2>
|
|
1306
|
+
<h2>Readiness check</h2>
|
|
1307
1307
|
<div class="orrline">
|
|
1308
1308
|
<span class="pill orr-${esc(orr.overall)}">${esc(orr.overall)}</span>
|
|
1309
|
-
<span class="disp">${esc(orr.disposition
|
|
1309
|
+
<span class="disp">${esc(dispositionWord(orr.disposition))}</span>
|
|
1310
1310
|
${target}
|
|
1311
1311
|
</div>
|
|
1312
1312
|
${failure}
|
|
1313
1313
|
${fix}
|
|
1314
1314
|
${findings}
|
|
1315
|
-
<p class="mute small">
|
|
1315
|
+
<p class="mute small">checks: ${orr.scorecards.pass} pass · ${orr.scorecards.fail} fail · ${orr.scorecards.partial} partial · ${orr.scorecards.notEvaluated} n/e${when} · refreshes every 30 min while the report runs</p>
|
|
1316
1316
|
${run}
|
|
1317
1317
|
</section>`;
|
|
1318
1318
|
}
|
|
1319
|
+
function dispositionWord(disposition) {
|
|
1320
|
+
switch (disposition) {
|
|
1321
|
+
case "go":
|
|
1322
|
+
return "safe to run";
|
|
1323
|
+
case "conditional":
|
|
1324
|
+
return "fix before production";
|
|
1325
|
+
case "no_go":
|
|
1326
|
+
return "not safe to run";
|
|
1327
|
+
default:
|
|
1328
|
+
return disposition.replace(/_/g, " ");
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1319
1331
|
function severityCls(severity) {
|
|
1320
1332
|
if (severity === "critical" || severity === "high")
|
|
1321
1333
|
return "orr-red";
|
|
@@ -1342,20 +1354,20 @@ function kpiTile(label, value, cls = "") {
|
|
|
1342
1354
|
function heroCertifyPanel(certify) {
|
|
1343
1355
|
if (!certify) {
|
|
1344
1356
|
return `<section class="panel hero-certify none" id="frag-hero-certify" aria-label="Certify">
|
|
1345
|
-
<h2>Certify -
|
|
1357
|
+
<h2>Certify - trust checklist for your agent</h2>
|
|
1346
1358
|
<div class="orrline">
|
|
1347
|
-
<span class="pill">not
|
|
1359
|
+
<span class="pill">not checked yet</span>
|
|
1348
1360
|
</div>
|
|
1349
|
-
<p class="mute small">No
|
|
1361
|
+
<p class="mute small">No check yet - use your coding tools with kya connected, then run kya certify.</p>
|
|
1350
1362
|
</section>`;
|
|
1351
1363
|
}
|
|
1352
1364
|
const state = certify.result === "pass" ? "pass" : "gap";
|
|
1353
1365
|
// Status legend as a definition list so each counter is explained on first
|
|
1354
1366
|
// glance, not hidden in a dense sentence.
|
|
1355
1367
|
const legend = `<dl class="cert-legend">
|
|
1356
|
-
<div><dt>
|
|
1357
|
-
<div><dt>
|
|
1358
|
-
<div><dt>Attested</dt><dd>your
|
|
1368
|
+
<div><dt>Failing</dt><dd>these rules fail right now - do these first</dd></div>
|
|
1369
|
+
<div><dt>No data</dt><dd>not enough local data to decide (never counts as a pass)</dd></div>
|
|
1370
|
+
<div><dt>Attested</dt><dd>your written statement, not verified</dd></div>
|
|
1359
1371
|
</dl>`;
|
|
1360
1372
|
// Attested summary: the hero's gap rows are gap-status only, so attestations
|
|
1361
1373
|
// surface as one line naming the first attested requirement (+N more) with
|
|
@@ -1365,7 +1377,7 @@ function heroCertifyPanel(certify) {
|
|
|
1365
1377
|
? ""
|
|
1366
1378
|
: firstAttested
|
|
1367
1379
|
? `<div class="att-line">attested: <code>${esc(firstAttested.id)}</code>${certify.attested > 1 ? ` (+${certify.attested - 1} more)` : ""} - "${expandableText("att-quote", firstAttested.attestation?.text ?? firstAttested.evidence, 100)}"</div>`
|
|
1368
|
-
: `<p class="att-line">attested: ${certify.attested} - your
|
|
1380
|
+
: `<p class="att-line">attested: ${certify.attested} - your written statement, not verified</p>`;
|
|
1369
1381
|
// topGaps arrives severity-ordered from the loader; cap defensively at 5.
|
|
1370
1382
|
// Each gap is an expandable <details> row: summary stays compact, the body
|
|
1371
1383
|
// reveals the full requirement title, what the status means, the unclipped
|
|
@@ -1380,9 +1392,9 @@ ${certify.topGaps
|
|
|
1380
1392
|
return ` <details class="req-row">
|
|
1381
1393
|
<summary><code>${esc(g.id)}</code> - ${esc(g.title)} <span class="sev ${esc(g.severity)}">${esc(g.severity)}</span></summary>
|
|
1382
1394
|
<div class="req-body">
|
|
1383
|
-
<p class="req-mean">
|
|
1384
|
-
${g.evidence ? `<p class="req-evi"><strong>
|
|
1385
|
-
<p class="req-cta"><strong>
|
|
1395
|
+
<p class="req-mean">This rule is failing right now, based on activity in the last ${certify.windowDays} days.</p>
|
|
1396
|
+
${g.evidence ? `<p class="req-evi"><strong>Why:</strong> ${esc(g.evidence)}</p>` : ""}
|
|
1397
|
+
<p class="req-cta"><strong>Fix it:</strong> <code>${esc(cmd)}</code> <span class="req-hint">(replace "…" with your answer, or fix the problem itself)</span></p>
|
|
1386
1398
|
</div>
|
|
1387
1399
|
</details>`;
|
|
1388
1400
|
})
|
|
@@ -1390,26 +1402,26 @@ ${certify.topGaps
|
|
|
1390
1402
|
</div>`;
|
|
1391
1403
|
// Call-to-action: one primary command and one secondary link to the detail tab.
|
|
1392
1404
|
const cta = `<div class="cert-cta">
|
|
1393
|
-
<a class="btn" href="#certify">
|
|
1394
|
-
<span class="mute small">or run <code>kya certify --open</code> for the signed
|
|
1405
|
+
<a class="btn" href="#certify">See all rules</a>
|
|
1406
|
+
<span class="mute small">or run <code>kya certify --open</code> for the signed proof file</span>
|
|
1395
1407
|
</div>`;
|
|
1396
1408
|
// Pill tones reuse the ORR palette: pass is green, gap is amber.
|
|
1397
1409
|
return `<section class="panel hero-certify ${state}" id="frag-hero-certify" aria-label="Certify">
|
|
1398
|
-
<h2>Certify -
|
|
1410
|
+
<h2>Certify - trust checklist for your agent</h2>
|
|
1399
1411
|
<div class="orrline">
|
|
1400
|
-
<span class="pill orr-${state === "pass" ? "green" : "amber"}">${esc(certify.result)}</span>
|
|
1412
|
+
<span class="pill orr-${state === "pass" ? "green" : "amber"}">${esc(certify.result === "pass" ? "pass" : "has gaps")}</span>
|
|
1401
1413
|
</div>
|
|
1402
1414
|
<div class="kpi-row">
|
|
1403
1415
|
${kpiTile("Pass", certify.pass, "ok")}
|
|
1404
|
-
${kpiTile("
|
|
1405
|
-
${kpiTile("
|
|
1416
|
+
${kpiTile("Failing", certify.gap, "warn")}
|
|
1417
|
+
${kpiTile("No data", certify.insufficientEvidence, "mute")}
|
|
1406
1418
|
${kpiTile("Attested", certify.attested, "mute")}
|
|
1407
1419
|
</div>
|
|
1408
1420
|
${legend}
|
|
1409
1421
|
${attestedLine}
|
|
1410
1422
|
${gaps}
|
|
1411
1423
|
${cta}
|
|
1412
|
-
<p class="mute small">live
|
|
1424
|
+
<p class="mute small">checked live - last ${certify.windowDays} days, ${certify.trailEvents} recorded events · the Certify tab lists every rule · <code>kya certify</code> writes the signed proof file</p>
|
|
1413
1425
|
</section>`;
|
|
1414
1426
|
}
|
|
1415
1427
|
/** Detail-table pill tones, reusing the existing pill/tone system: pass is
|
|
@@ -1482,7 +1494,7 @@ ${items}
|
|
|
1482
1494
|
})
|
|
1483
1495
|
.join("\n");
|
|
1484
1496
|
return `<section class="panel certify-detail" aria-label="Certify detail">
|
|
1485
|
-
<h2>Every
|
|
1497
|
+
<h2>Every rule, checked live</h2>
|
|
1486
1498
|
${sections}
|
|
1487
1499
|
</section>`;
|
|
1488
1500
|
}
|
|
@@ -1496,7 +1508,7 @@ function verdictsHeroCard(c, total) {
|
|
|
1496
1508
|
const pct = (n) => (total > 0 ? Math.round((n / total) * 100) : 0);
|
|
1497
1509
|
const mixRow = (label, n, fill) => `<div class="mix-row"><span class="mix-lab">${label}</span><span class="mix-bar"><span class="mix-fill ${fill}" style="width:${pct(n)}%"></span></span><span class="mix-num">${n}</span></div>`;
|
|
1498
1510
|
return `<section class="panel" aria-label="Verdicts">
|
|
1499
|
-
<h2>
|
|
1511
|
+
<h2>Decisions</h2>
|
|
1500
1512
|
<div class="mix">
|
|
1501
1513
|
${mixRow("Allow", c.allow, "ok")}
|
|
1502
1514
|
${mixRow("Deny", c.deny, "bad")}
|
|
@@ -1558,7 +1570,7 @@ ${topRuns
|
|
|
1558
1570
|
.join("\n")}
|
|
1559
1571
|
</ul>`;
|
|
1560
1572
|
return `<section class="panel" aria-label="Showback">
|
|
1561
|
-
<h2>
|
|
1573
|
+
<h2>Token use and cost (information only)</h2>
|
|
1562
1574
|
<div class="kpi-row">
|
|
1563
1575
|
${kpiTile("Tokens in", report.totalTokensIn)}
|
|
1564
1576
|
${kpiTile("Tokens out", report.totalTokensOut)}
|
|
@@ -1627,7 +1639,7 @@ function importHeroCard(card, nowMs, live) {
|
|
|
1627
1639
|
: "";
|
|
1628
1640
|
return `<section class="panel" aria-label="Import">
|
|
1629
1641
|
<h2>Import</h2>
|
|
1630
|
-
<p class="line">Nothing imported yet.
|
|
1642
|
+
<p class="line">Nothing imported yet. Import records from LangSmith, Langfuse, Phoenix, OTel, AGT, ADR Sensor, or OpenShell so they appear in this report and feed Certify.</p>
|
|
1631
1643
|
<p class="mute small">CLI: <code>kya import --from langsmith|langfuse|phoenix|otel|adr-sensor|openshell FILE</code> · <code>kya import --from agt</code> reads the AGT audit log</p>
|
|
1632
1644
|
${form}
|
|
1633
1645
|
</section>`;
|
|
@@ -1661,7 +1673,7 @@ function discoveryHeroCard(card, nowMs, live) {
|
|
|
1661
1673
|
: "";
|
|
1662
1674
|
return `<section class="panel" aria-label="Discovery">
|
|
1663
1675
|
<h2>Discovery</h2>
|
|
1664
|
-
<p class="line">No
|
|
1676
|
+
<p class="line">No scan yet. Discovery lists the AI agents, IDE extensions, desktop apps, model runtimes, and MCP servers on this computer. Home folder paths are hidden.</p>
|
|
1665
1677
|
<p class="mute small">CLI: <code>kya discover</code></p>
|
|
1666
1678
|
${scan}
|
|
1667
1679
|
</section>`;
|
|
@@ -1701,14 +1713,14 @@ function investigateHeroCard(card, nowMs, live) {
|
|
|
1701
1713
|
: "";
|
|
1702
1714
|
return `<section class="panel" aria-label="Investigations">
|
|
1703
1715
|
<h2>Investigations</h2>
|
|
1704
|
-
<p class="line">No investigation
|
|
1716
|
+
<p class="line">No investigation yet. Detectors scan the record for repeated blocks, incidents, and risky patterns, then write fix notes for your coding agent.</p>
|
|
1705
1717
|
<p class="mute small">CLI: <code>kya investigate</code></p>
|
|
1706
1718
|
${run}
|
|
1707
1719
|
</section>`;
|
|
1708
1720
|
}
|
|
1709
1721
|
const findingCount = card.findings.reduce((n, f) => n + f.count, 0);
|
|
1710
1722
|
const rows = card.findings.length === 0
|
|
1711
|
-
? `<p class="line">No
|
|
1723
|
+
? `<p class="line">No problems found - the record looks clean for all detectors.</p>`
|
|
1712
1724
|
: `<ul class="rows">
|
|
1713
1725
|
${card.findings
|
|
1714
1726
|
.map((f) => ` <li>${severityPill(f.severity)} <code>${esc(f.detectorId)}</code> - ${esc(f.title)} <span class="cnt">× ${f.count}</span></li>`)
|
|
@@ -1741,7 +1753,7 @@ function alertsPanel(card, nowMs, live) {
|
|
|
1741
1753
|
: "";
|
|
1742
1754
|
return `<section class="panel" aria-label="Alerts">
|
|
1743
1755
|
<h2>Alerts</h2>
|
|
1744
|
-
<p class="line">No
|
|
1756
|
+
<p class="line">No alerts sent yet. kya sends a webhook when a call is blocked or needs approval. Every attempt is logged here.</p>
|
|
1745
1757
|
<p class="mute small">send a test alert to see the pipeline work (it uses a temporary loopback receiver until you configure <code>notify.webhooks</code> in .kya/config.json or set <code>KYA_NOTIFY_WEBHOOK</code>)</p>
|
|
1746
1758
|
${test}
|
|
1747
1759
|
</section>`;
|
|
@@ -1784,8 +1796,8 @@ function otelPanel(stats, nowMs, live) {
|
|
|
1784
1796
|
<div id="report-action-result-otel-test"></div>`
|
|
1785
1797
|
: "";
|
|
1786
1798
|
return `<section class="panel" aria-label="OTel export">
|
|
1787
|
-
<h2>OTel
|
|
1788
|
-
<p class="line">
|
|
1799
|
+
<h2>Export to your monitoring (OTel)</h2>
|
|
1800
|
+
<p class="line">Nothing exported yet. kya can send decision events to your monitoring system (any OTLP/HTTP collector). Sent and failed counts appear here.</p>
|
|
1789
1801
|
<p class="mute small">test the export now (it uses a temporary loopback collector until you configure <code>otlpExport.endpoint</code> in .kya/config.json or set <code>KYA_OTLP_EXPORT_ENDPOINT</code>)</p>
|
|
1790
1802
|
${test}
|
|
1791
1803
|
</section>`;
|
|
@@ -1795,7 +1807,7 @@ function otelPanel(stats, nowMs, live) {
|
|
|
1795
1807
|
? `last export ${esc(relativeTime(stats.lastExportAt, nowMs))} · `
|
|
1796
1808
|
: "";
|
|
1797
1809
|
return `<section class="panel" aria-label="OTel export">
|
|
1798
|
-
<h2>OTel
|
|
1810
|
+
<h2>Export to your monitoring (OTel)</h2>
|
|
1799
1811
|
<div class="orrline">${endpoint}</div>
|
|
1800
1812
|
<div class="kpi-row">
|
|
1801
1813
|
${kpiTile("Spans sent", stats.spansSent)}
|
|
@@ -1917,7 +1929,7 @@ export function buildLiveFragments(model, livePaged) {
|
|
|
1917
1929
|
showbackHeroCard(model.showback, events),
|
|
1918
1930
|
].join("\n "),
|
|
1919
1931
|
overviewPanels: zoneBody([dashboardPanel(dashboard), sessionsPanel(agg, nowMs, changes), reasonsPanel(agg)], "No activity in this window yet - analytics appear once events land."),
|
|
1920
|
-
certifyBody: zoneBody([model.certify ? certifyDetailPanel(model.certify.requirements) : ""], "No
|
|
1932
|
+
certifyBody: zoneBody([model.certify ? certifyDetailPanel(model.certify.requirements) : ""], "No check yet - use your coding tools with kya connected, then run kya certify."),
|
|
1921
1933
|
orrPanel: orrPanel(model.orr, nowMs, livePaged),
|
|
1922
1934
|
changesList: changesPanel(changes, nowMs),
|
|
1923
1935
|
},
|
|
@@ -2855,7 +2867,7 @@ export function renderReceiptMarkdown(model) {
|
|
|
2855
2867
|
lines.push(`- \`${mdInline(g.id)}\` - ${mdText(g.title)} (${mdText(g.severity)})`);
|
|
2856
2868
|
}
|
|
2857
2869
|
}
|
|
2858
|
-
lines.push(`live
|
|
2870
|
+
lines.push(`checked live - last ${c.windowDays} days, ${c.trailEvents} recorded events · the Certify tab lists every rule · kya certify writes the gap report and signed proof file`, "");
|
|
2859
2871
|
}
|
|
2860
2872
|
const db = computeDashboard(model.events);
|
|
2861
2873
|
if (db.verdictMix.total > 0) {
|
|
@@ -2927,7 +2939,7 @@ export function renderReceiptMarkdown(model) {
|
|
|
2927
2939
|
lines.push("## Sessions", ...agg.sessions.map((s) => `- \`${mdInline(s.sessionId)}\` - ${s.events} event${s.events === 1 ? "" : "s"}, worst: ${s.worst}, last ${relativeTime(s.lastTs, nowMs)}`), "");
|
|
2928
2940
|
}
|
|
2929
2941
|
if (agg.reasons.length > 0) {
|
|
2930
|
-
lines.push("##
|
|
2942
|
+
lines.push("## Why calls were allowed, held, or blocked", ...agg.reasons.map((r) => `- ${mdText(r.label)} × ${r.count}`), "");
|
|
2931
2943
|
}
|
|
2932
2944
|
if (model.gate) {
|
|
2933
2945
|
const g = model.gate;
|
|
@@ -2953,7 +2965,7 @@ export function renderReceiptMarkdown(model) {
|
|
|
2953
2965
|
}
|
|
2954
2966
|
const hosts = model.wiredHosts ?? [];
|
|
2955
2967
|
if (hosts.some((h) => h.wired !== "none")) {
|
|
2956
|
-
lines.push("##
|
|
2968
|
+
lines.push("## Connected tools");
|
|
2957
2969
|
for (const h of hosts) {
|
|
2958
2970
|
if (h.recipeOnly) {
|
|
2959
2971
|
lines.push(`- ${mdText(h.label)} - manual setup (docs recipe)`);
|
|
@@ -2980,16 +2992,16 @@ export function renderReceiptMarkdown(model) {
|
|
|
2980
2992
|
}
|
|
2981
2993
|
if (model.orr) {
|
|
2982
2994
|
const orr = model.orr;
|
|
2983
|
-
lines.push("##
|
|
2995
|
+
lines.push("## Readiness check", `overall: ${orr.overall} · disposition: ${orr.disposition.replace(/_/g, " ")}${orr.targetName ? ` · checked ${mdText(orr.targetName)}` : ""}`);
|
|
2984
2996
|
if (orr.primaryFailureMode)
|
|
2985
|
-
lines.push(`
|
|
2997
|
+
lines.push(`Main problem: ${mdText(orr.primaryFailureMode)}`);
|
|
2986
2998
|
if (orr.mostUrgentFix)
|
|
2987
|
-
lines.push(`
|
|
2988
|
-
lines.push(`
|
|
2999
|
+
lines.push(`First fix to make: ${mdText(orr.mostUrgentFix)}`);
|
|
3000
|
+
lines.push(`checks: ${orr.scorecards.pass} pass · ${orr.scorecards.fail} fail · ${orr.scorecards.partial} partial · ${orr.scorecards.notEvaluated} n/e${orr.generatedAt ? ` · ${relativeTime(orr.generatedAt, nowMs)}` : ""}`, "");
|
|
2989
3001
|
}
|
|
2990
3002
|
if (model.showback) {
|
|
2991
3003
|
const sb = model.showback;
|
|
2992
|
-
lines.push("##
|
|
3004
|
+
lines.push("## Token use and cost (information only)", `${sb.totalTokensIn} tokens in · ${sb.totalTokensOut} tokens out · ${usdText(sb.estimatedUsd)}`);
|
|
2993
3005
|
const topRuns = [...sb.perRun]
|
|
2994
3006
|
.sort((a, b) => (b.estimatedUsd ?? -1) - (a.estimatedUsd ?? -1))
|
|
2995
3007
|
.slice(0, 5);
|
|
@@ -3022,7 +3034,7 @@ export function renderReceiptMarkdown(model) {
|
|
|
3022
3034
|
}
|
|
3023
3035
|
if (model.otel) {
|
|
3024
3036
|
const ot = model.otel;
|
|
3025
|
-
lines.push("## OTel
|
|
3037
|
+
lines.push("## Export to your monitoring (OTel)", `${ot.endpoint ? `\`${mdInline(ot.endpoint)}\` · ` : ""}${ot.spansSent} sent · ${ot.spansFailed} failed${ot.lastExportAt ? ` · last export ${relativeTime(ot.lastExportAt, nowMs)}` : ""}`, "");
|
|
3026
3038
|
}
|
|
3027
3039
|
const md = `${lines.join("\n")}\n`;
|
|
3028
3040
|
assertNoSecrets(md);
|