site-agent-pro 1.0.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.
Files changed (81) hide show
  1. package/README.md +689 -0
  2. package/dist/auth/credentialStore.js +62 -0
  3. package/dist/auth/inbox.js +193 -0
  4. package/dist/auth/profile.js +379 -0
  5. package/dist/auth/runner.js +1124 -0
  6. package/dist/backend/dashboardData.js +194 -0
  7. package/dist/backend/runArtifacts.js +48 -0
  8. package/dist/backend/runRepository.js +93 -0
  9. package/dist/bin.js +2 -0
  10. package/dist/cli/backfillSiteChecks.js +143 -0
  11. package/dist/cli/run.js +309 -0
  12. package/dist/cli/trade.js +69 -0
  13. package/dist/config.js +199 -0
  14. package/dist/core/agentProfiles.js +55 -0
  15. package/dist/core/aggregateReport.js +382 -0
  16. package/dist/core/audit.js +30 -0
  17. package/dist/core/customTaskSuite.js +148 -0
  18. package/dist/core/evaluator.js +217 -0
  19. package/dist/core/executor.js +788 -0
  20. package/dist/core/fallbackReport.js +335 -0
  21. package/dist/core/formHeuristics.js +411 -0
  22. package/dist/core/gameplaySummary.js +164 -0
  23. package/dist/core/interaction.js +202 -0
  24. package/dist/core/pageState.js +201 -0
  25. package/dist/core/planner.js +1669 -0
  26. package/dist/core/processSubmissionBatch.js +204 -0
  27. package/dist/core/runAuditJob.js +170 -0
  28. package/dist/core/runner.js +2352 -0
  29. package/dist/core/siteBrief.js +107 -0
  30. package/dist/core/siteChecks.js +1526 -0
  31. package/dist/core/taskDirectives.js +279 -0
  32. package/dist/core/taskHeuristics.js +263 -0
  33. package/dist/dashboard/client.js +1256 -0
  34. package/dist/dashboard/contracts.js +95 -0
  35. package/dist/dashboard/narrative.js +277 -0
  36. package/dist/dashboard/server.js +458 -0
  37. package/dist/dashboard/theme.js +888 -0
  38. package/dist/index.js +84 -0
  39. package/dist/llm/client.js +188 -0
  40. package/dist/paystack/account.js +123 -0
  41. package/dist/paystack/client.js +100 -0
  42. package/dist/paystack/index.js +13 -0
  43. package/dist/paystack/test-paystack.js +83 -0
  44. package/dist/paystack/transfer.js +138 -0
  45. package/dist/paystack/types.js +74 -0
  46. package/dist/paystack/webhook.js +121 -0
  47. package/dist/prompts/browserAgent.js +124 -0
  48. package/dist/prompts/reviewer.js +71 -0
  49. package/dist/reporting/clickReplay.js +290 -0
  50. package/dist/reporting/html.js +930 -0
  51. package/dist/reporting/markdown.js +238 -0
  52. package/dist/reporting/template.js +1141 -0
  53. package/dist/schemas/types.js +361 -0
  54. package/dist/submissions/customTasks.js +196 -0
  55. package/dist/submissions/html.js +770 -0
  56. package/dist/submissions/model.js +56 -0
  57. package/dist/submissions/publicUrl.js +76 -0
  58. package/dist/submissions/service.js +74 -0
  59. package/dist/submissions/store.js +37 -0
  60. package/dist/submissions/types.js +65 -0
  61. package/dist/trade/engine.js +241 -0
  62. package/dist/trade/evm/erc20.js +44 -0
  63. package/dist/trade/extractor.js +148 -0
  64. package/dist/trade/policy.js +35 -0
  65. package/dist/trade/session.js +31 -0
  66. package/dist/trade/types.js +107 -0
  67. package/dist/trade/validator.js +148 -0
  68. package/dist/utils/files.js +59 -0
  69. package/dist/utils/log.js +24 -0
  70. package/dist/utils/playwrightCompat.js +14 -0
  71. package/dist/utils/time.js +3 -0
  72. package/dist/wallet/provider.js +345 -0
  73. package/dist/wallet/relay.js +129 -0
  74. package/dist/wallet/wallet.js +178 -0
  75. package/docs/01-installation.md +134 -0
  76. package/docs/02-running-your-first-audit.md +136 -0
  77. package/docs/03-configuration.md +233 -0
  78. package/docs/04-how-the-agent-thinks.md +41 -0
  79. package/docs/05-extending-personas-and-tasks.md +42 -0
  80. package/docs/06-hardening-for-production.md +92 -0
  81. package/package.json +60 -0
@@ -0,0 +1,930 @@
1
+ import { buildStructuredReviewTemplate, labelForCoverageStatus, labelForMetricStatus } from "./template.js";
2
+ import { DASHBOARD_HEAD_TAGS } from "../dashboard/theme.js";
3
+ function escapeHtml(value) {
4
+ return value
5
+ .replaceAll("&", "&")
6
+ .replaceAll("<", "&lt;")
7
+ .replaceAll(">", "&gt;")
8
+ .replaceAll('"', "&quot;")
9
+ .replaceAll("'", "&#39;");
10
+ }
11
+ function formatLogTime(value, timeZone) {
12
+ const parsed = new Date(value);
13
+ if (Number.isNaN(parsed.getTime())) {
14
+ return value;
15
+ }
16
+ return parsed.toLocaleTimeString(undefined, {
17
+ hour: "2-digit",
18
+ minute: "2-digit",
19
+ second: "2-digit",
20
+ ...(timeZone ? { timeZone } : {})
21
+ });
22
+ }
23
+ function describeAction(entry) {
24
+ const target = entry.decision.target.trim();
25
+ switch (entry.decision.action) {
26
+ case "click":
27
+ return target ? `Clicked "${target}"` : "Clicked a visible element";
28
+ case "type":
29
+ return target ? `Typed into "${target}"` : "Typed into a visible field";
30
+ case "scroll":
31
+ return "Scrolled the page";
32
+ case "wait":
33
+ return "Waited for the page to respond";
34
+ case "back":
35
+ return "Went back one page";
36
+ case "extract":
37
+ return "Captured a page snapshot";
38
+ case "trade":
39
+ return "Executed the wallet trade handoff";
40
+ case "stop":
41
+ return "Stopped the path";
42
+ default:
43
+ return entry.decision.action;
44
+ }
45
+ }
46
+ function deriveSessionLog(taskResults) {
47
+ return taskResults
48
+ .flatMap((taskResult) => taskResult.history)
49
+ .sort((left, right) => new Date(left.time).getTime() - new Date(right.time).getTime())
50
+ .map((entry) => ({
51
+ action: describeAction(entry),
52
+ outcome: entry.result.note,
53
+ task: entry.task,
54
+ time: entry.time,
55
+ url: entry.url
56
+ }));
57
+ }
58
+ function renderSimpleList(items) {
59
+ if (items.length === 0) {
60
+ return `<li class="empty-item">None recorded.</li>`;
61
+ }
62
+ return items.map((item) => `<li>${escapeHtml(item)}</li>`).join("");
63
+ }
64
+ function renderInstructionText(value) {
65
+ const normalized = value?.trim();
66
+ if (!normalized) {
67
+ return "";
68
+ }
69
+ return `<p class="instruction-text">${escapeHtml(normalized)}</p>`;
70
+ }
71
+ function formatTaskOutcome(status) {
72
+ switch (status) {
73
+ case "success":
74
+ return "Succeeded";
75
+ case "partial_success":
76
+ return "Partially Succeeded";
77
+ case "failed":
78
+ default:
79
+ return "Failed";
80
+ }
81
+ }
82
+ function renderToolList(items) {
83
+ if (items.length === 0) {
84
+ return "";
85
+ }
86
+ return `<div class="tool-row">${items.map((item) => `<span class="tool-chip">${escapeHtml(item)}</span>`).join("")}</div>`;
87
+ }
88
+ function renderStatusBadge(status) {
89
+ return `<span class="status-badge status-badge--${status}">${escapeHtml(labelForMetricStatus(status))}</span>`;
90
+ }
91
+ function renderCoverageBadge(status) {
92
+ return `<span class="status-badge status-badge--${status === "verified" ? "good" : status === "inferred" ? "warning" : "blocked"}">${escapeHtml(labelForCoverageStatus(status))}</span>`;
93
+ }
94
+ function renderMetricTable(metrics) {
95
+ return `
96
+ <div class="table-wrap">
97
+ <table class="metric-table">
98
+ <thead>
99
+ <tr>
100
+ <th>Metric</th>
101
+ <th>Value</th>
102
+ <th>Status</th>
103
+ <th>Coverage</th>
104
+ </tr>
105
+ </thead>
106
+ <tbody>
107
+ ${metrics
108
+ .map((metric) => `
109
+ <tr>
110
+ <td>${escapeHtml(metric.label)}</td>
111
+ <td>${escapeHtml(metric.value)}</td>
112
+ <td>${renderStatusBadge(metric.status)}</td>
113
+ <td>${renderCoverageBadge(metric.verification)}</td>
114
+ </tr>
115
+ `)
116
+ .join("")}
117
+ </tbody>
118
+ </table>
119
+ </div>
120
+ `;
121
+ }
122
+ function renderMetricGroups(groups) {
123
+ return groups
124
+ .map((group) => `
125
+ <div class="subsection">
126
+ <h3>${escapeHtml(group.title)}</h3>
127
+ ${renderMetricTable(group.metrics)}
128
+ </div>
129
+ `)
130
+ .join("");
131
+ }
132
+ function renderCoverageBlock(coverage) {
133
+ return `
134
+ <div class="summary-card" style="margin-bottom: 0.95rem;">
135
+ <div class="task-head">
136
+ <h3>Coverage</h3>
137
+ ${renderCoverageBadge(coverage.status)}
138
+ </div>
139
+ <p>${escapeHtml(coverage.summary)}</p>
140
+ ${coverage.evidence.length > 0 ? `<ul class="bullet-list" style="margin-top: 0.8rem;">${renderSimpleList(coverage.evidence)}</ul>` : ""}
141
+ ${coverage.blockers.length > 0 ? `<p style="margin-top: 0.8rem; color: var(--red);">${escapeHtml(`Blockers: ${coverage.blockers.join(" ")}`)}</p>` : ""}
142
+ </div>
143
+ `;
144
+ }
145
+ export function renderHtmlReport(args) {
146
+ const template = buildStructuredReviewTemplate({
147
+ website: args.website,
148
+ report: args.report,
149
+ taskResults: args.taskResults,
150
+ accessibility: args.accessibility,
151
+ siteChecks: args.siteChecks,
152
+ rawEvents: args.rawEvents,
153
+ startedAt: args.startedAt,
154
+ mobile: args.mobile,
155
+ timeZone: args.timeZone
156
+ });
157
+ const sessionLog = deriveSessionLog(args.taskResults);
158
+ return `<!doctype html>
159
+ <html lang="en">
160
+ <head>
161
+ <meta charset="utf-8" />
162
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
163
+ <title>Task Execution Output</title>
164
+ ${DASHBOARD_HEAD_TAGS}
165
+ <style>
166
+ :root {
167
+ color-scheme: dark;
168
+ --bg: #0c0c10;
169
+ --surface: #13131a;
170
+ --surface2: #1a1a24;
171
+ --surface3: #22222d;
172
+ --border: rgba(255,255,255,0.07);
173
+ --border2: rgba(255,255,255,0.12);
174
+ --text: #e8e8f0;
175
+ --muted: #6b6b80;
176
+ --accent: #00d4aa;
177
+ --accent-dim: rgba(0,212,170,0.12);
178
+ --accent-glow: rgba(0,212,170,0.25);
179
+ --blue: #4d9fff;
180
+ --blue-dim: rgba(77,159,255,0.12);
181
+ --amber: #f5a623;
182
+ --amber-dim: rgba(245,166,35,0.12);
183
+ --red: #ff5555;
184
+ --red-dim: rgba(255,85,85,0.12);
185
+ --font-sans: "Syne", sans-serif;
186
+ --font-mono: "IBM Plex Mono", monospace;
187
+ }
188
+
189
+ * { box-sizing: border-box; }
190
+ html, body { margin: 0; min-height: 100%; }
191
+
192
+ body {
193
+ color: var(--text);
194
+ font-family: var(--font-sans);
195
+ background:
196
+ radial-gradient(circle at top left, rgba(0,212,170,0.07), transparent 22%),
197
+ radial-gradient(circle at bottom right, rgba(77,159,255,0.08), transparent 24%),
198
+ var(--bg);
199
+ }
200
+
201
+ .report-topbar {
202
+ position: sticky;
203
+ top: 0;
204
+ z-index: 20;
205
+ background: rgba(12,12,16,0.85);
206
+ backdrop-filter: blur(12px);
207
+ border-bottom: 1px solid var(--border);
208
+ }
209
+
210
+ .report-topbar__inner {
211
+ width: min(1180px, calc(100vw - 2rem));
212
+ margin: 0 auto;
213
+ padding: 12px 0;
214
+ display: flex;
215
+ align-items: center;
216
+ justify-content: space-between;
217
+ gap: 12px;
218
+ }
219
+
220
+ .report-brand {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 10px;
224
+ min-width: 0;
225
+ }
226
+
227
+ .logo-mark {
228
+ width: 30px;
229
+ height: 30px;
230
+ border-radius: 8px;
231
+ background: var(--accent);
232
+ position: relative;
233
+ flex-shrink: 0;
234
+ }
235
+
236
+ .logo-mark::after {
237
+ content: "";
238
+ position: absolute;
239
+ inset: 10px;
240
+ border-radius: 50%;
241
+ background: var(--bg);
242
+ }
243
+
244
+ .brand-name {
245
+ color: var(--text);
246
+ font-size: 15px;
247
+ font-weight: 700;
248
+ letter-spacing: -0.02em;
249
+ }
250
+
251
+ .brand-sub {
252
+ color: var(--muted);
253
+ font-family: var(--font-mono);
254
+ font-size: 11px;
255
+ }
256
+
257
+ .topbar-chip {
258
+ display: inline-flex;
259
+ align-items: center;
260
+ gap: 6px;
261
+ padding: 6px 10px;
262
+ border-radius: 999px;
263
+ border: 1px solid var(--border);
264
+ color: var(--muted);
265
+ background: var(--surface2);
266
+ font-family: var(--font-mono);
267
+ font-size: 11px;
268
+ }
269
+
270
+ .page {
271
+ width: min(1180px, calc(100vw - 2rem));
272
+ margin: 0 auto;
273
+ padding: 1rem 0 2.5rem;
274
+ display: grid;
275
+ gap: 1.15rem;
276
+ }
277
+
278
+ .section {
279
+ background: var(--surface);
280
+ border: 1px solid var(--border);
281
+ border-radius: 12px;
282
+ padding: 1.4rem;
283
+ }
284
+
285
+ .hero {
286
+ display: grid;
287
+ grid-template-columns: minmax(0, 1.45fr) minmax(260px, 0.55fr);
288
+ gap: 1.15rem;
289
+ align-items: start;
290
+ }
291
+
292
+ .eyebrow {
293
+ margin: 0 0 0.5rem;
294
+ color: var(--accent);
295
+ font-size: 0.78rem;
296
+ font-weight: 700;
297
+ letter-spacing: 0.16em;
298
+ text-transform: uppercase;
299
+ font-family: var(--font-mono);
300
+ }
301
+
302
+ h1, h2, h3 {
303
+ margin: 0;
304
+ font-family: var(--font-sans);
305
+ color: var(--text);
306
+ letter-spacing: -0.02em;
307
+ }
308
+
309
+ h1 {
310
+ font-size: clamp(2rem, 4vw, 3rem);
311
+ line-height: 0.98;
312
+ }
313
+
314
+ h2 {
315
+ font-size: clamp(1.35rem, 3vw, 2rem);
316
+ margin-bottom: 0.95rem;
317
+ }
318
+
319
+ h3 {
320
+ font-size: 1.02rem;
321
+ }
322
+
323
+ p {
324
+ margin: 0;
325
+ line-height: 1.65;
326
+ color: rgba(232, 232, 240, 0.82);
327
+ }
328
+
329
+ .lead {
330
+ margin-top: 0.95rem;
331
+ color: var(--text);
332
+ font-size: 1rem;
333
+ }
334
+
335
+ .meta-grid,
336
+ .card-grid,
337
+ .split-grid,
338
+ .priority-grid,
339
+ .log-grid,
340
+ .task-grid {
341
+ display: grid;
342
+ gap: 0.9rem;
343
+ }
344
+
345
+ .meta-grid {
346
+ margin-top: 1rem;
347
+ grid-template-columns: repeat(2, minmax(0, 1fr));
348
+ }
349
+
350
+ .card-grid,
351
+ .split-grid,
352
+ .priority-grid {
353
+ grid-template-columns: repeat(2, minmax(0, 1fr));
354
+ }
355
+
356
+ .task-grid,
357
+ .log-grid {
358
+ grid-template-columns: 1fr;
359
+ }
360
+
361
+ .summary-card,
362
+ .priority-card,
363
+ .task-card,
364
+ .log-card {
365
+ border: 1px solid var(--border);
366
+ border-radius: 10px;
367
+ background: var(--surface2);
368
+ padding: 1rem;
369
+ }
370
+
371
+ .score-card {
372
+ border: 1px solid var(--border2);
373
+ border-radius: 12px;
374
+ background: linear-gradient(160deg, rgba(0,212,170,0.16), rgba(77,159,255,0.10));
375
+ padding: 1.2rem;
376
+ }
377
+
378
+ .score-number {
379
+ font-size: clamp(2.3rem, 4vw, 3.1rem);
380
+ line-height: 1;
381
+ color: var(--accent);
382
+ font-family: var(--font-sans);
383
+ }
384
+
385
+ .score-label {
386
+ margin-top: 0.4rem;
387
+ font-size: 0.94rem;
388
+ }
389
+
390
+ .meta-label {
391
+ color: var(--muted);
392
+ font-size: 0.72rem;
393
+ text-transform: uppercase;
394
+ letter-spacing: 0.08em;
395
+ font-family: var(--font-mono);
396
+ }
397
+
398
+ .meta-value {
399
+ margin-top: 0.3rem;
400
+ color: var(--text);
401
+ font-size: 1rem;
402
+ }
403
+
404
+ .tool-row {
405
+ display: flex;
406
+ flex-wrap: wrap;
407
+ gap: 0.55rem;
408
+ margin-bottom: 0.95rem;
409
+ }
410
+
411
+ .tool-chip {
412
+ display: inline-flex;
413
+ align-items: center;
414
+ border-radius: 999px;
415
+ padding: 0.35rem 0.7rem;
416
+ border: 1px solid rgba(0,212,170,0.24);
417
+ background: var(--accent-dim);
418
+ color: var(--accent);
419
+ font-size: 0.76rem;
420
+ font-weight: 500;
421
+ font-family: var(--font-mono);
422
+ }
423
+
424
+ .table-wrap {
425
+ overflow-x: auto;
426
+ }
427
+
428
+ .metric-table {
429
+ width: 100%;
430
+ border-collapse: collapse;
431
+ }
432
+
433
+ .metric-table th,
434
+ .metric-table td {
435
+ padding: 0.8rem 0.75rem;
436
+ text-align: left;
437
+ border-top: 1px solid var(--border);
438
+ vertical-align: top;
439
+ }
440
+
441
+ .metric-table thead th {
442
+ border-top: none;
443
+ color: var(--muted);
444
+ font-size: 0.74rem;
445
+ font-weight: 500;
446
+ letter-spacing: 0.05em;
447
+ text-transform: uppercase;
448
+ font-family: var(--font-mono);
449
+ }
450
+
451
+ .metric-table tbody td:first-child {
452
+ color: var(--text);
453
+ font-weight: 700;
454
+ }
455
+
456
+ .metric-table tbody td {
457
+ color: rgba(232, 232, 240, 0.86);
458
+ }
459
+
460
+ .status-badge {
461
+ display: inline-flex;
462
+ align-items: center;
463
+ padding: 0.32rem 0.68rem;
464
+ border-radius: 999px;
465
+ font-size: 0.78rem;
466
+ font-weight: 700;
467
+ letter-spacing: 0.04em;
468
+ text-transform: uppercase;
469
+ }
470
+
471
+ .status-badge--good {
472
+ color: var(--accent);
473
+ background: var(--accent-dim);
474
+ }
475
+
476
+ .status-badge--warning {
477
+ color: var(--amber);
478
+ background: var(--amber-dim);
479
+ }
480
+
481
+ .status-badge--poor {
482
+ color: var(--red);
483
+ background: var(--red-dim);
484
+ }
485
+
486
+ .status-badge--blocked {
487
+ color: var(--red);
488
+ background: var(--red-dim);
489
+ }
490
+
491
+ .status-badge--neutral,
492
+ .status-badge--not_measured {
493
+ color: var(--muted);
494
+ background: rgba(255,255,255,0.06);
495
+ }
496
+
497
+ .bullet-list {
498
+ margin: 0;
499
+ padding-left: 1.2rem;
500
+ display: grid;
501
+ gap: 0.6rem;
502
+ }
503
+
504
+ .bullet-list li {
505
+ line-height: 1.6;
506
+ }
507
+
508
+ .bullet-list li,
509
+ .task-body,
510
+ .log-card p {
511
+ color: var(--text);
512
+ }
513
+
514
+ .empty-item {
515
+ color: var(--muted) !important;
516
+ }
517
+
518
+ .section-copy {
519
+ display: grid;
520
+ gap: 0.9rem;
521
+ }
522
+
523
+ .subsection {
524
+ display: grid;
525
+ gap: 0.75rem;
526
+ }
527
+
528
+ .priority-card h3,
529
+ .summary-card h3 {
530
+ margin-bottom: 0.7rem;
531
+ }
532
+
533
+ .task-head {
534
+ display: flex;
535
+ justify-content: space-between;
536
+ align-items: center;
537
+ gap: 1rem;
538
+ margin-bottom: 0.7rem;
539
+ }
540
+
541
+ .task-status {
542
+ display: inline-flex;
543
+ align-items: center;
544
+ padding: 0.3rem 0.65rem;
545
+ border-radius: 999px;
546
+ background: var(--accent-dim);
547
+ color: var(--accent);
548
+ font-size: 0.8rem;
549
+ font-weight: 500;
550
+ text-transform: uppercase;
551
+ font-family: var(--font-mono);
552
+ }
553
+
554
+ .task-body {
555
+ margin-bottom: 0.75rem;
556
+ }
557
+
558
+ .instruction-text {
559
+ white-space: pre-wrap;
560
+ color: var(--text);
561
+ }
562
+
563
+ .task-evidence {
564
+ margin: 0;
565
+ padding-left: 1.15rem;
566
+ display: grid;
567
+ gap: 0.45rem;
568
+ }
569
+
570
+ .log-card {
571
+ display: grid;
572
+ gap: 0.35rem;
573
+ }
574
+
575
+ .log-action {
576
+ color: var(--text);
577
+ font-weight: 700;
578
+ }
579
+
580
+ .log-meta {
581
+ display: flex;
582
+ flex-wrap: wrap;
583
+ gap: 0.55rem;
584
+ color: var(--muted);
585
+ font-size: 0.84rem;
586
+ }
587
+
588
+ .mono {
589
+ font-family: var(--font-mono);
590
+ font-size: 0.86rem;
591
+ }
592
+
593
+ @media (max-width: 980px) {
594
+ .hero,
595
+ .meta-grid,
596
+ .card-grid,
597
+ .split-grid,
598
+ .priority-grid {
599
+ grid-template-columns: 1fr;
600
+ }
601
+ }
602
+
603
+ @media (max-width: 720px) {
604
+ .page {
605
+ width: min(100vw, calc(100vw - 1rem));
606
+ }
607
+
608
+ .report-topbar__inner {
609
+ width: min(100vw, calc(100vw - 1rem));
610
+ align-items: flex-start;
611
+ flex-direction: column;
612
+ }
613
+
614
+ .section {
615
+ padding: 1.1rem;
616
+ border-radius: 12px;
617
+ }
618
+ }
619
+ </style>
620
+ </head>
621
+ <body>
622
+ <header class="report-topbar">
623
+ <div class="report-topbar__inner">
624
+ <div class="report-brand">
625
+ <div class="logo-mark"></div>
626
+ <div>
627
+ <div class="brand-name">site-agent-pro</div>
628
+ <div class="brand-sub">Task execution output</div>
629
+ </div>
630
+ </div>
631
+ <div class="topbar-chip">${escapeHtml(args.runId ? `Run ${args.runId}` : "Generated task output")}</div>
632
+ </div>
633
+ </header>
634
+ <main class="page">
635
+ <section class="section hero">
636
+ <div>
637
+ <p class="eyebrow">Task Execution Output</p>
638
+ <h1>${escapeHtml(args.website)}</h1>
639
+ <p class="lead">${escapeHtml(template.executiveSummary.summary)}</p>
640
+ <div class="meta-grid">
641
+ <div class="summary-card">
642
+ <div class="meta-label">Run Date</div>
643
+ <div class="meta-value">${escapeHtml(template.executiveSummary.auditDate)}</div>
644
+ </div>
645
+ <div class="summary-card">
646
+ <div class="meta-label">Visitor Lens</div>
647
+ <div class="meta-value">${escapeHtml(args.persona)}</div>
648
+ </div>
649
+ <div class="summary-card">
650
+ <div class="meta-label">Run Mode</div>
651
+ <div class="meta-value">${escapeHtml(args.mobile ? "Mobile-sized browser run" : "Desktop-sized browser run")}</div>
652
+ </div>
653
+ <div class="summary-card">
654
+ <div class="meta-label">Run ID</div>
655
+ <div class="meta-value mono">${escapeHtml(args.runId ?? "Not provided")}</div>
656
+ </div>
657
+ </div>
658
+ </div>
659
+ <div class="score-card">
660
+ <div class="meta-label">Overall Score</div>
661
+ <div class="score-number">${escapeHtml(template.executiveSummary.overallScore)}</div>
662
+ <div class="score-label">Accepted-task execution plus supporting diagnostics.</div>
663
+ </div>
664
+ </section>
665
+
666
+ ${args.clickReplayArtifact
667
+ ? `
668
+ <section class="section">
669
+ <h2>Activity Replay</h2>
670
+ <div class="summary-card" style="padding: 0; overflow: hidden; background: #000; display: flex; justify-content: center; align-items: center; min-height: 400px; border: 1px solid var(--border2);">
671
+ <img src="${args.clickReplayArtifact}" alt="Activity replay" style="max-width: 100%; max-height: 720px; display: block;" />
672
+ </div>
673
+ <p class="meta-label" style="margin-top: 0.8rem; text-align: center;">Animated replay of actions captured during this run.</p>
674
+ </section>
675
+ `
676
+ : ""}
677
+
678
+ <section class="section">
679
+ <h2>1. Task Summary</h2>
680
+ <div class="card-grid">
681
+ <div class="summary-card">
682
+ <h3>Key Strengths</h3>
683
+ <ul class="bullet-list">${renderSimpleList(template.executiveSummary.keyStrengths)}</ul>
684
+ </div>
685
+ <div class="summary-card">
686
+ <h3>Critical Issues</h3>
687
+ <ul class="bullet-list">${renderSimpleList(template.executiveSummary.criticalIssues)}</ul>
688
+ </div>
689
+ </div>
690
+ <div class="summary-card" style="margin-top: 0.9rem;">
691
+ <h3>Business Impact</h3>
692
+ <p>${escapeHtml(template.executiveSummary.businessImpact)}</p>
693
+ </div>
694
+ ${args.siteBrief
695
+ ? `
696
+ <div class="summary-card" style="margin-top: 0.9rem;">
697
+ <h3>What This Site Appears To Do</h3>
698
+ <p>${escapeHtml(args.siteBrief.summary)}</p>
699
+ ${args.siteBrief.intendedUserActions.length > 0 ? `<ul class="bullet-list" style="margin-top: 0.8rem;">${renderSimpleList(args.siteBrief.intendedUserActions)}</ul>` : ""}
700
+ </div>
701
+ `
702
+ : ""}
703
+ ${(args.acceptedTasks?.length ?? 0) > 0 || args.instructionText?.trim()
704
+ ? `
705
+ <div class="summary-card" style="margin-top: 0.9rem;">
706
+ <h3>Instructions I Followed</h3>
707
+ ${(args.acceptedTasks?.length ?? 0) > 0 ? `<ul class="bullet-list">${renderSimpleList(args.acceptedTasks ?? [])}</ul>` : renderInstructionText(args.instructionText)}
708
+ </div>
709
+ `
710
+ : ""}
711
+ <div class="summary-card" style="margin-top: 0.9rem;">
712
+ <h3>Accepted Task Outcomes</h3>
713
+ <ul class="bullet-list">
714
+ ${renderSimpleList(args.report.task_results.map((task) => `${task.name}: ${formatTaskOutcome(task.status)}. ${task.reason}`))}
715
+ </ul>
716
+ </div>
717
+ ${args.report.gameplay_summary
718
+ ? `
719
+ <div class="summary-card" style="margin-top: 0.9rem;">
720
+ <h3>Gameplay Results</h3>
721
+ <p>${escapeHtml(args.report.gameplay_summary.summary)}</p>
722
+ <ul class="bullet-list">
723
+ ${renderSimpleList([
724
+ `Rounds requested: ${args.report.gameplay_summary.roundsRequested}`,
725
+ `Rounds recorded: ${args.report.gameplay_summary.roundsRecorded}`,
726
+ `Wins: ${args.report.gameplay_summary.wins}`,
727
+ `Losses: ${args.report.gameplay_summary.losses}`,
728
+ `Draws: ${args.report.gameplay_summary.draws}`,
729
+ `Inconclusive rounds: ${args.report.gameplay_summary.inconclusiveRounds}`,
730
+ args.report.gameplay_summary.howToPlayConfirmed ? "How-to-play guidance was visibly confirmed." : "How-to-play guidance was not clearly confirmed.",
731
+ args.report.gameplay_summary.replayConfirmed ? "Replay or restart controls were visibly confirmed." : "Replay or restart controls were not clearly confirmed."
732
+ ])}
733
+ </ul>
734
+ </div>
735
+ `
736
+ : ""}
737
+ </section>
738
+
739
+ <section class="section">
740
+ <h2>2. Performance Analysis</h2>
741
+ ${renderToolList(template.performance.tools)}
742
+ ${renderCoverageBlock(template.performance.coverage)}
743
+ ${renderMetricTable(template.performance.metrics)}
744
+ <div class="split-grid" style="margin-top: 0.95rem;">
745
+ <div class="summary-card">
746
+ <h3>Insights</h3>
747
+ <ul class="bullet-list">${renderSimpleList(template.performance.insights)}</ul>
748
+ </div>
749
+ <div class="summary-card">
750
+ <h3>Recommendations</h3>
751
+ <ul class="bullet-list">${renderSimpleList(template.performance.recommendations)}</ul>
752
+ </div>
753
+ </div>
754
+ </section>
755
+
756
+ <section class="section">
757
+ <h2>3. SEO Audit</h2>
758
+ ${renderToolList(template.seo.tools)}
759
+ ${renderCoverageBlock(template.seo.coverage)}
760
+ <div class="section-copy">
761
+ ${renderMetricGroups(template.seo.groups)}
762
+ </div>
763
+ <div class="summary-card" style="margin-top: 0.95rem;">
764
+ <h3>Recommendations</h3>
765
+ <ul class="bullet-list">${renderSimpleList(template.seo.recommendations)}</ul>
766
+ </div>
767
+ </section>
768
+
769
+ <section class="section">
770
+ <h2>4. UI/UX Evaluation</h2>
771
+ ${renderCoverageBlock(template.uiux.coverage)}
772
+ ${renderMetricTable(template.uiux.metrics)}
773
+ <div class="split-grid" style="margin-top: 0.95rem;">
774
+ <div class="summary-card">
775
+ <h3>Key Issues</h3>
776
+ <ul class="bullet-list">${renderSimpleList(template.uiux.issues)}</ul>
777
+ </div>
778
+ <div class="summary-card">
779
+ <h3>Recommendations</h3>
780
+ <ul class="bullet-list">${renderSimpleList(template.uiux.recommendations)}</ul>
781
+ </div>
782
+ </div>
783
+ </section>
784
+
785
+ <section class="section">
786
+ <h2>5. Security Analysis</h2>
787
+ ${renderToolList(template.security.tools)}
788
+ ${renderCoverageBlock(template.security.coverage)}
789
+ ${renderMetricTable(template.security.metrics)}
790
+ <div class="summary-card" style="margin-top: 0.95rem;">
791
+ <h3>Recommendations</h3>
792
+ <ul class="bullet-list">${renderSimpleList(template.security.recommendations)}</ul>
793
+ </div>
794
+ </section>
795
+
796
+ <section class="section">
797
+ <h2>6. Technical Health</h2>
798
+ ${renderCoverageBlock(template.technicalHealth.coverage)}
799
+ ${renderMetricTable(template.technicalHealth.metrics)}
800
+ <div class="summary-card" style="margin-top: 0.95rem;">
801
+ <h3>Recommendations</h3>
802
+ <ul class="bullet-list">${renderSimpleList(template.technicalHealth.recommendations)}</ul>
803
+ </div>
804
+ </section>
805
+
806
+ <section class="section">
807
+ <h2>7. Mobile Optimization</h2>
808
+ ${renderCoverageBlock(template.mobileOptimization.coverage)}
809
+ ${renderMetricTable(template.mobileOptimization.metrics)}
810
+ <div class="summary-card" style="margin-top: 0.95rem;">
811
+ <h3>Recommendations</h3>
812
+ <ul class="bullet-list">${renderSimpleList(template.mobileOptimization.recommendations)}</ul>
813
+ </div>
814
+ </section>
815
+
816
+ <section class="section">
817
+ <h2>8. Content Quality</h2>
818
+ ${renderCoverageBlock(template.contentQuality.coverage)}
819
+ ${renderMetricTable(template.contentQuality.metrics)}
820
+ <div class="summary-card" style="margin-top: 0.95rem;">
821
+ <h3>Recommendations</h3>
822
+ <ul class="bullet-list">${renderSimpleList(template.contentQuality.recommendations)}</ul>
823
+ </div>
824
+ </section>
825
+
826
+ <section class="section">
827
+ <h2>9. Conversion Optimization (CRO)</h2>
828
+ ${renderCoverageBlock(template.cro.coverage)}
829
+ ${renderMetricTable(template.cro.metrics)}
830
+ <div class="summary-card" style="margin-top: 0.95rem;">
831
+ <h3>Recommendations</h3>
832
+ <ul class="bullet-list">${renderSimpleList(template.cro.recommendations)}</ul>
833
+ </div>
834
+ </section>
835
+
836
+ <section class="section">
837
+ <h2>10. Action Plan (Prioritized)</h2>
838
+ <div class="priority-grid">
839
+ <div class="priority-card">
840
+ <h3>High Priority</h3>
841
+ <ul class="bullet-list">${renderSimpleList(template.actionPlan.high)}</ul>
842
+ </div>
843
+ <div class="priority-card">
844
+ <h3>Medium Priority</h3>
845
+ <ul class="bullet-list">${renderSimpleList(template.actionPlan.medium)}</ul>
846
+ </div>
847
+ </div>
848
+ <div class="priority-card" style="margin-top: 0.9rem;">
849
+ <h3>Low Priority</h3>
850
+ <ul class="bullet-list">${renderSimpleList(template.actionPlan.low)}</ul>
851
+ </div>
852
+ </section>
853
+
854
+ <section class="section">
855
+ <h2>11. Final Score Breakdown</h2>
856
+ ${renderMetricTable(template.scoreBreakdown.map((item) => ({
857
+ label: item.category,
858
+ value: item.score,
859
+ status: item.category === "Overall"
860
+ ? (args.report.overall_score >= 8 ? "good" : args.report.overall_score >= 6 ? "warning" : "poor")
861
+ : "warning",
862
+ verification: item.category === "Overall" ? "verified" : "inferred"
863
+ })))}
864
+ </section>
865
+
866
+ <section class="section">
867
+ <h2>12. Site Agent Pro Notes</h2>
868
+ <div class="card-grid">
869
+ <div class="summary-card">
870
+ <h3>Confidence Level</h3>
871
+ <p>${escapeHtml(template.agentNotes.confidence)}</p>
872
+ </div>
873
+ <div class="summary-card">
874
+ <h3>Data Sources Used</h3>
875
+ <ul class="bullet-list">${renderSimpleList(template.agentNotes.dataSources)}</ul>
876
+ </div>
877
+ </div>
878
+ <div class="summary-card" style="margin-top: 0.9rem;">
879
+ <h3>Limitations of Analysis</h3>
880
+ <ul class="bullet-list">${renderSimpleList(template.agentNotes.limitations)}</ul>
881
+ </div>
882
+ </section>
883
+
884
+ <section class="section">
885
+ <h2>Appendix: Task Evidence</h2>
886
+ <div class="task-grid">
887
+ ${args.report.task_results
888
+ .map((task) => `
889
+ <article class="task-card">
890
+ <div class="task-head">
891
+ <h3>${escapeHtml(task.name)}</h3>
892
+ <span class="task-status">${escapeHtml(task.status)}</span>
893
+ </div>
894
+ <p class="task-body">${escapeHtml(task.reason)}</p>
895
+ <ul class="task-evidence">
896
+ ${renderSimpleList(task.evidence)}
897
+ </ul>
898
+ </article>
899
+ `)
900
+ .join("")}
901
+ </div>
902
+ </section>
903
+
904
+ ${sessionLog.length > 0
905
+ ? `
906
+ <section class="section">
907
+ <h2>Appendix: Interaction Log</h2>
908
+ <div class="log-grid">
909
+ ${sessionLog
910
+ .slice(0, 18)
911
+ .map((item) => `
912
+ <article class="log-card">
913
+ <div class="log-action">${escapeHtml(item.action)}</div>
914
+ <div class="log-meta">
915
+ <span>${escapeHtml(item.task)}</span>
916
+ <span>${escapeHtml(formatLogTime(item.time, args.timeZone))}</span>
917
+ <span class="mono">${escapeHtml(item.url)}</span>
918
+ </div>
919
+ <p>${escapeHtml(item.outcome)}</p>
920
+ </article>
921
+ `)
922
+ .join("")}
923
+ </div>
924
+ </section>
925
+ `
926
+ : ""}
927
+ </main>
928
+ </body>
929
+ </html>`;
930
+ }