@inflexa-ai/harness 0.17.0 → 0.18.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 (221) hide show
  1. package/dist/agents/report-builder.d.ts +2 -0
  2. package/dist/agents/report-builder.js +2 -0
  3. package/dist/app/spawn-report-session.d.ts +85 -0
  4. package/dist/app/spawn-report-session.js +83 -0
  5. package/dist/contracts/chat-events.d.ts +9 -0
  6. package/dist/contracts/index.d.ts +6 -2
  7. package/dist/contracts/index.js +3 -1
  8. package/dist/contracts/organ-system.d.ts +49 -0
  9. package/dist/contracts/organ-system.js +67 -0
  10. package/dist/contracts/report-blocks.d.ts +564 -0
  11. package/dist/contracts/report-blocks.js +117 -0
  12. package/dist/contracts/report-reference.d.ts +459 -0
  13. package/dist/contracts/report-reference.js +256 -0
  14. package/dist/contracts/schemas/chat-events.d.ts +2 -0
  15. package/dist/contracts/schemas/chat-events.js +2 -0
  16. package/dist/contracts/severity.d.ts +15 -0
  17. package/dist/contracts/severity.js +10 -0
  18. package/dist/contracts/target-dossier.d.ts +8675 -12972
  19. package/dist/contracts/target-dossier.js +534 -591
  20. package/dist/data/safety-panel-schema.d.ts +18 -5
  21. package/dist/data/safety-panel-schema.js +4 -4
  22. package/dist/execution/artifact-metadata.js +1 -0
  23. package/dist/execution/report-runner.js +1 -3
  24. package/dist/execution/run-synthesis.js +2 -0
  25. package/dist/index.d.ts +6 -0
  26. package/dist/index.js +19 -0
  27. package/dist/lib/async-utils.d.ts +16 -4
  28. package/dist/lib/async-utils.js +20 -15
  29. package/dist/lib/host-concurrency.js +1 -0
  30. package/dist/loop/__fixtures__/scripted-provider.js +23 -2
  31. package/dist/loop/run-agent.js +58 -10
  32. package/dist/loop/tool-detail.d.ts +40 -0
  33. package/dist/loop/tool-detail.js +35 -3
  34. package/dist/loop/types.d.ts +13 -0
  35. package/dist/memory/thread-history.d.ts +10 -0
  36. package/dist/memory/thread-history.js +12 -1
  37. package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +1 -1
  38. package/dist/prompts/target-assessment/briefs/executive-recommendation.js +5 -5
  39. package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +1 -1
  40. package/dist/prompts/target-assessment/briefs/liability-bullets.js +1 -1
  41. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +1 -1
  42. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +11 -3
  43. package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +1 -0
  44. package/dist/prompts/target-assessment/investigation/adversarial-critique.js +67 -0
  45. package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +1 -0
  46. package/dist/prompts/target-assessment/investigation/claim-reverification.js +49 -0
  47. package/dist/prompts/target-assessment/investigation/index.d.ts +3 -0
  48. package/dist/prompts/target-assessment/investigation/index.js +3 -0
  49. package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +1 -0
  50. package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +49 -0
  51. package/dist/prompts/target-assessment/tox-voice/index.d.ts +1 -1
  52. package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +8 -5
  53. package/dist/prompts/target-assessment/tox-voice/vocabulary.js +9 -20
  54. package/dist/providers/__fixtures__/session.d.ts +5 -4
  55. package/dist/providers/__fixtures__/session.js +4 -4
  56. package/dist/report-model/block-walk.d.ts +56 -0
  57. package/dist/report-model/block-walk.js +89 -0
  58. package/dist/report-model/draft-finish.d.ts +60 -0
  59. package/dist/report-model/draft-finish.js +56 -0
  60. package/dist/report-model/draft-operations.d.ts +125 -0
  61. package/dist/report-model/draft-operations.js +341 -0
  62. package/dist/report-model/draft-read.d.ts +59 -0
  63. package/dist/report-model/draft-read.js +109 -0
  64. package/dist/report-model/draft.d.ts +51 -0
  65. package/dist/report-model/draft.js +32 -0
  66. package/dist/report-model/fixture-resolver.d.ts +8 -0
  67. package/dist/report-model/fixture-resolver.js +252 -0
  68. package/dist/report-model/mint-snapshot.d.ts +30 -0
  69. package/dist/report-model/mint-snapshot.js +40 -0
  70. package/dist/report-model/reference-resolver.d.ts +113 -0
  71. package/dist/report-model/reference-resolver.js +60 -0
  72. package/dist/report-model/structural-validation.d.ts +22 -0
  73. package/dist/report-model/structural-validation.js +68 -0
  74. package/dist/report-model/validate.d.ts +40 -0
  75. package/dist/report-model/validate.js +80 -0
  76. package/dist/report-render/chart.d.ts +33 -0
  77. package/dist/report-render/chart.js +538 -0
  78. package/dist/report-render/page.d.ts +127 -0
  79. package/dist/report-render/page.js +307 -0
  80. package/dist/report-render/references.d.ts +32 -0
  81. package/dist/report-render/references.js +44 -0
  82. package/dist/report-render/render.d.ts +25 -0
  83. package/dist/report-render/render.js +125 -0
  84. package/dist/report-render/script-json.d.ts +13 -0
  85. package/dist/report-render/script-json.js +15 -0
  86. package/dist/report-render/types.d.ts +39 -0
  87. package/dist/report-render/types.js +10 -0
  88. package/dist/report-render/views/chart-view.d.ts +11 -0
  89. package/dist/report-render/views/chart-view.js +23 -0
  90. package/dist/report-render/views/page-view.d.ts +20 -0
  91. package/dist/report-render/views/page-view.js +49 -0
  92. package/dist/report-render/views/prose.d.ts +28 -0
  93. package/dist/report-render/views/prose.js +75 -0
  94. package/dist/report-render/views/references-view.d.ts +20 -0
  95. package/dist/report-render/views/references-view.js +64 -0
  96. package/dist/report-render/views/values.d.ts +42 -0
  97. package/dist/report-render/views/values.js +43 -0
  98. package/dist/runtime/boot.d.ts +3 -3
  99. package/dist/runtime/boot.js +15 -4
  100. package/dist/runtime/dbos.d.ts +4 -4
  101. package/dist/state/artifacts.d.ts +16 -0
  102. package/dist/state/artifacts.js +18 -0
  103. package/dist/state/init.js +31 -0
  104. package/dist/state/purge-analysis.js +1 -0
  105. package/dist/state/report-versions.d.ts +132 -0
  106. package/dist/state/report-versions.js +194 -0
  107. package/dist/tasks/data-profile.js +1 -0
  108. package/dist/tools/bio/chembl.js +1 -0
  109. package/dist/tools/bio/comptox.js +1 -0
  110. package/dist/tools/bio/drug-gene-interactions.js +1 -0
  111. package/dist/tools/bio/gene-disease-evidence.js +1 -0
  112. package/dist/tools/bio/gene-preclinical-profile.js +1 -0
  113. package/dist/tools/bio/keys.d.ts +3 -2
  114. package/dist/tools/bio/keys.js +3 -2
  115. package/dist/tools/bio/lookup-annotation.js +1 -0
  116. package/dist/tools/bio/opentargets.d.ts +3 -3
  117. package/dist/tools/bio/opentargets.js +4 -3
  118. package/dist/tools/bio/pubchem.js +1 -0
  119. package/dist/tools/bio/search-clinical-trials.js +1 -0
  120. package/dist/tools/bio/search-faers.js +1 -0
  121. package/dist/tools/bio/search-geo-datasets.js +1 -0
  122. package/dist/tools/bio/search-interactions.js +1 -0
  123. package/dist/tools/bio/target-safety.d.ts +4 -6
  124. package/dist/tools/bio/target-safety.js +7 -6
  125. package/dist/tools/define-tool.d.ts +19 -12
  126. package/dist/tools/define-tool.js +4 -2
  127. package/dist/tools/display/show-user.js +1 -0
  128. package/dist/tools/iterate-report.js +2 -0
  129. package/dist/tools/lib/chembl-client.d.ts +2 -0
  130. package/dist/tools/lib/chembl-client.js +1 -0
  131. package/dist/tools/lib/monarch-client.d.ts +42 -0
  132. package/dist/tools/lib/monarch-client.js +100 -0
  133. package/dist/tools/report/build-report.js +1 -0
  134. package/dist/tools/report/mint-preview-url.js +1 -0
  135. package/dist/tools/report/preview-snapshot.js +1 -0
  136. package/dist/tools/report/submit-report.js +1 -0
  137. package/dist/tools/report/version-fs.js +4 -0
  138. package/dist/tools/report-authoring/authoring-tools.d.ts +134 -0
  139. package/dist/tools/report-authoring/authoring-tools.js +352 -0
  140. package/dist/tools/research/context7-docs.js +2 -0
  141. package/dist/tools/research/generate-analogy-report.js +1 -0
  142. package/dist/tools/research/generate-plan.js +3 -0
  143. package/dist/tools/research/inspect-data-profile.js +4 -0
  144. package/dist/tools/research/literature-reviewer.js +1 -0
  145. package/dist/tools/research/search-arxiv.js +1 -0
  146. package/dist/tools/research/search-github-repos.js +1 -0
  147. package/dist/tools/research/search-semantic-scholar.js +1 -0
  148. package/dist/tools/sandbox/list-available-packages.js +23 -0
  149. package/dist/tools/sandbox/list-available-refs.js +36 -0
  150. package/dist/tools/sandbox/report-blocker.js +1 -0
  151. package/dist/tools/sandbox/skills.js +2 -0
  152. package/dist/tools/workspace/file-stat.js +1 -0
  153. package/dist/tools/workspace/show-file.js +2 -0
  154. package/dist/tools/workspace/show-plan.js +1 -0
  155. package/dist/workflows/execute-analysis.d.ts +5 -1
  156. package/dist/workflows/execute-analysis.js +5 -5
  157. package/dist/workflows/execute-target-assessment.d.ts +13 -2
  158. package/dist/workflows/execute-target-assessment.js +78 -16
  159. package/dist/workflows/target-assessment/assemblers/index.d.ts +1 -1
  160. package/dist/workflows/target-assessment/assemblers/index.js +1 -1
  161. package/dist/workflows/target-assessment/assemblers/literature.d.ts +9 -27
  162. package/dist/workflows/target-assessment/assemblers/literature.js +32 -48
  163. package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +17 -9
  164. package/dist/workflows/target-assessment/assemblers/orchestrator.js +195 -85
  165. package/dist/workflows/target-assessment/assemblers/safety.d.ts +70 -48
  166. package/dist/workflows/target-assessment/assemblers/safety.js +125 -42
  167. package/dist/workflows/target-assessment/collectors/index.d.ts +5 -2
  168. package/dist/workflows/target-assessment/collectors/index.js +48 -5
  169. package/dist/workflows/target-assessment/coverage.d.ts +43 -27
  170. package/dist/workflows/target-assessment/coverage.js +33 -29
  171. package/dist/workflows/target-assessment/fanout/index.js +1 -0
  172. package/dist/workflows/target-assessment/investigation/index.d.ts +97 -0
  173. package/dist/workflows/target-assessment/investigation/index.js +419 -0
  174. package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +26 -5
  175. package/dist/workflows/target-assessment/lib/approval-precedents.js +95 -29
  176. package/dist/workflows/target-assessment/lib/citation-validator.js +1 -1
  177. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +2 -2
  178. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +2 -3
  179. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -4
  180. package/dist/workflows/target-assessment/lib/compute-derived.d.ts +6 -5
  181. package/dist/workflows/target-assessment/lib/compute-derived.js +14 -5
  182. package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +5 -6
  183. package/dist/workflows/target-assessment/lib/family-complex-supplement.js +2 -3
  184. package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +72 -0
  185. package/dist/workflows/target-assessment/lib/fda-label-safety.js +166 -0
  186. package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -9
  187. package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -10
  188. package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +34 -0
  189. package/dist/workflows/target-assessment/lib/hpo-organ-map.js +63 -0
  190. package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +19 -0
  191. package/dist/workflows/target-assessment/lib/impc-organ-map.js +39 -0
  192. package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +24 -14
  193. package/dist/workflows/target-assessment/lib/meddra-organ-map.js +59 -47
  194. package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +1 -1
  195. package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +2 -3
  196. package/dist/workflows/target-assessment/lib/regulatory-actions.js +33 -4
  197. package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +44 -0
  198. package/dist/workflows/target-assessment/lib/safety-corroboration.js +273 -0
  199. package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +5 -7
  200. package/dist/workflows/target-assessment/lib/target-identity-filter.js +5 -7
  201. package/dist/workflows/target-assessment/metrics.d.ts +1 -6
  202. package/dist/workflows/target-assessment/metrics.js +1 -13
  203. package/dist/workflows/target-assessment/phase4-assemble.d.ts +2147 -807
  204. package/dist/workflows/target-assessment/phase4-assemble.js +19 -19
  205. package/dist/workflows/target-assessment/phase5-persist.d.ts +20 -21
  206. package/dist/workflows/target-assessment/phase5-persist.js +56 -173
  207. package/dist/workflows/target-assessment/schemas.d.ts +46 -1
  208. package/dist/workflows/target-assessment/schemas.js +19 -0
  209. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +1 -0
  210. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +2 -0
  211. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +26 -62
  212. package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -4
  213. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +27 -154
  214. package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -6
  215. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -6
  216. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -18
  217. package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +1 -0
  218. package/dist/workflows/target-assessment/synthesis/index.d.ts +52 -5
  219. package/dist/workflows/target-assessment/synthesis/index.js +4 -1
  220. package/dist/workflows/target-assessment/synthesis-probes.d.ts +3 -3
  221. package/package.json +4 -1
@@ -0,0 +1,307 @@
1
+ /**
2
+ * The page constants: the CDN pins, the inline style, the ECharts theme, and the chart bootstrap.
3
+ *
4
+ * These constants are copies. The old template pack at `templates/report-html` holds the source, and the
5
+ * old render path owns that directory. The renderer never reads that directory at run time, thus the
6
+ * output stays a pure function of its inputs. A copy that drifts is a maintenance cost, and it is the
7
+ * price of a page with no local asset and no template engine.
8
+ */
9
+ /**
10
+ * The head references: the Tailwind runtime, the ECharts runtime, the two fonts, and the preconnect. Each
11
+ * tag carries the pinned version and, where the source pins one, the integrity hash. The bytes are exact
12
+ * copies, thus the page stays deterministic and the hashes stay valid.
13
+ */
14
+ export const CDN_HEAD = `<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.2/dist/index.global.min.js" integrity="sha384-shaHAtPgz0ulP7R/YmFe0nZtC8FxdhJPi73vwJQcADVttxvHLJBJt/pjzkLSbIvL" crossorigin="anonymous"></script>
15
+ <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.1/dist/echarts.min.js" integrity="sha384-Mx5lkUEQPM1pOJCwFtUICyX45KNojXbkWdYhkKUKsbv391mavbfoAmONbzkgYPzR" crossorigin="anonymous"></script>
16
+ <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
17
+ <link href="https://cdn.jsdelivr.net/npm/@fontsource-variable/space-grotesk@5.1.1/index.css" rel="stylesheet" integrity="sha384-b3Dtcpab/ltw+oDnVHstCCa9ENEb2hKouCSRAfb8o0ROkWOb2uNWYG6+l7HGrHmG" crossorigin="anonymous">
18
+ <link href="https://cdn.jsdelivr.net/npm/@fontsource-variable/ibm-plex-mono@5.1.1/index.css" rel="stylesheet">`;
19
+ /**
20
+ * The style rules of the page. The renderer inlines them in one `<style>` block, thus the page holds the
21
+ * whole design token set and every component rule with no external stylesheet.
22
+ */
23
+ export const PAGE_CSS = `/* ── Color Palette ────────────────────────────────────── */
24
+ :root {
25
+ /* Primary Scale */
26
+ --color-primary-50: #f0f1fe; /* Tag bg, CTA section bg */
27
+ --color-primary-100: #dde0fc;
28
+ --color-primary-200: #bcc2f9; /* Badge borders, corner accent default */
29
+ --color-primary-300: #9ba5f5; /* Corner button accent default */
30
+ --color-primary-400: #7987f0; /* Section illustration icons */
31
+ --color-primary-500: #576dea; /* Primary action, section labels, links */
32
+ --color-primary-600: #4458d4; /* Button hover */
33
+ --color-primary-700: #3545b0; /* Tag text on primary-50 bg */
34
+
35
+ /* Text */
36
+ --color-heading: #0f172a; /* slate-900 — all headings */
37
+ --color-text: #475569; /* slate-600 — body text */
38
+ --color-text-secondary: #64748b; /* slate-500 — captions, metadata */
39
+ --color-text-muted: #94a3b8; /* slate-400 — timestamps, footnotes */
40
+
41
+ /* Surface */
42
+ --color-bg: #ffffff; /* Page background (white) */
43
+ --color-bg-alt: #f8fafc; /* Alternating section bg (slate-50) */
44
+ --color-card: #ffffff; /* Cards (white) */
45
+ --color-border: #e2e8f0; /* slate-200 — card borders, dividers */
46
+ --color-border-hover: #cbd5e1; /* slate-300 — hover borders */
47
+
48
+ /* Data Visualization */
49
+ --color-up: #ef4444; /* Red 500 — upregulated, positive */
50
+ --color-down: #576dea; /* Primary 500 — downregulated, negative */
51
+ --color-ns: #94a3b8; /* Slate 400 — not significant */
52
+
53
+ /* Semantic (Tags — solid bg + colored text) */
54
+ --color-high-bg: #f0fdf4; /* green-50 */
55
+ --color-high-text: #15803d; /* green-700 */
56
+ --color-medium-bg: #fffbeb; /* amber-50 */
57
+ --color-medium-text:#b45309; /* amber-700 */
58
+ --color-low-bg: #fef2f2; /* red-50 */
59
+ --color-low-text: #b91c1c; /* red-700 */
60
+
61
+ /* Stat card accent colors */
62
+ --color-stat-primary: #576dea;
63
+ --color-stat-green: #22c55e;
64
+ --color-stat-red: #ef4444;
65
+ --color-stat-purple: #a78bfa;
66
+ --color-stat-amber: #f59e0b;
67
+ }
68
+
69
+ /* ── Typography ───────────────────────────────────────── */
70
+ body {
71
+ font-family: 'Space Grotesk Variable', system-ui, sans-serif;
72
+ font-size: 16px;
73
+ line-height: 1.6;
74
+ color: var(--color-text);
75
+ }
76
+
77
+ .font-mono, code, .gene {
78
+ font-family: 'IBM Plex Mono Variable', ui-monospace, monospace;
79
+ }
80
+
81
+ /* Rule: Headings use font-sans (Space Grotesk), never font-mono.
82
+ Labels, tags, badges, data, product names always use font-mono (IBM Plex Mono). */
83
+
84
+ /* ── Corner Accent Cards ─────────────────────────────── */
85
+ .corner-accents {
86
+ position: relative;
87
+ border: 1px solid var(--color-border);
88
+ background: var(--color-card);
89
+ /* No border-radius — square corners */
90
+ }
91
+ .corner-accents::before,
92
+ .corner-accents::after {
93
+ content: '';
94
+ position: absolute;
95
+ width: 20px;
96
+ height: 20px;
97
+ transition: all 0.3s ease;
98
+ pointer-events: none;
99
+ }
100
+ .corner-accents::before {
101
+ top: -1px; left: -1px;
102
+ border-top: 2px solid var(--color-primary-200);
103
+ border-left: 2px solid var(--color-primary-200);
104
+ }
105
+ .corner-accents::after {
106
+ bottom: -1px; right: -1px;
107
+ border-bottom: 2px solid var(--color-primary-200);
108
+ border-right: 2px solid var(--color-primary-200);
109
+ }
110
+ .corner-accents:hover::before,
111
+ .corner-accents:hover::after {
112
+ width: 28px;
113
+ height: 28px;
114
+ border-color: var(--color-primary-500);
115
+ }
116
+ .corner-accents:hover {
117
+ box-shadow: 0 4px 12px -4px rgba(15, 23, 42, 0.08);
118
+ }
119
+
120
+ /* ── Window Chrome Panel ─────────────────────────────── */
121
+ /* Exception: uses rounded-xl (only rounded component) */
122
+ .window-chrome {
123
+ border-radius: 12px;
124
+ border: 1px solid var(--color-border);
125
+ background: var(--color-card);
126
+ box-shadow: 0 8px 30px -8px rgba(15,23,42,0.08), 0 4px 12px -4px rgba(15,23,42,0.04);
127
+ transition: all 0.5s ease;
128
+ }
129
+ .window-chrome:hover {
130
+ transform: translateY(-4px);
131
+ box-shadow: 0 25px 50px -12px rgba(15,23,42,0.18), 0 12px 24px -8px rgba(15,23,42,0.1);
132
+ border-color: var(--color-border-hover);
133
+ }
134
+ .window-chrome .chrome-dots span { transition: background-color 0.3s ease; }
135
+ .window-chrome:hover .chrome-dots .dot-1 { background-color: #fb7185; }
136
+ .window-chrome:hover .chrome-dots .dot-2 { background-color: #fbbf24; }
137
+ .window-chrome:hover .chrome-dots .dot-3 { background-color: #4ade80; }
138
+
139
+ /* ── Terminal Card ───────────────────────────────────── */
140
+ .terminal-card {
141
+ position: relative;
142
+ overflow: hidden;
143
+ }
144
+ .terminal-card .terminal-header {
145
+ background: #1e293b; /* slate-800 */
146
+ }
147
+ .terminal-card .terminal-body-dark {
148
+ background: #0f172a; /* slate-900 */
149
+ color: #e2e8f0;
150
+ }
151
+ .terminal-card::after {
152
+ content: '';
153
+ position: absolute;
154
+ inset: 0;
155
+ pointer-events: none;
156
+ opacity: 0;
157
+ background: repeating-linear-gradient(
158
+ 0deg, transparent, transparent 2px,
159
+ rgba(148, 163, 184, 0.03) 2px, rgba(148, 163, 184, 0.03) 4px
160
+ );
161
+ transition: opacity 0.3s ease;
162
+ }
163
+ .terminal-card:hover::after { opacity: 1; }
164
+
165
+ /* ── Block Progress Bar ──────────────────────────────── */
166
+ /* Usage: <span class="block-progress"><span class="filled-high">████████</span><span class="empty">░░</span> <span class="score">0.82</span></span> */
167
+ .block-progress {
168
+ font-family: 'IBM Plex Mono Variable', ui-monospace, monospace;
169
+ font-size: 14px;
170
+ }
171
+ .block-progress .filled-high { color: var(--color-primary-500); } /* ≥ 0.8 */
172
+ .block-progress .filled-mid { color: #f59e0b; } /* ≥ 0.6 */
173
+ .block-progress .filled-low { color: #f43f5e; } /* < 0.6 */
174
+ .block-progress .empty { color: #e2e8f0; }
175
+ .block-progress .score { font-size: 12px; color: #94a3b8; margin-left: 8px; }
176
+
177
+ /* ── Sidebar Navigation ──────────────────────────────── */
178
+ /* When a sidebar is rendered (#report-sidebar), the body shifts right on lg+
179
+ to make room. Styling matches the corner-accent / mono-label language. */
180
+ @media (min-width: 1024px) {
181
+ body:has(#report-sidebar) { padding-left: 15rem; }
182
+ }
183
+ .sidebar-link {
184
+ border-left: 2px solid transparent;
185
+ }
186
+ .sidebar-link.active {
187
+ color: var(--color-primary-500);
188
+ background-color: var(--color-primary-50);
189
+ border-left-color: var(--color-primary-500);
190
+ font-weight: 600;
191
+ }
192
+
193
+ /* ── Insight Box ─────────────────────────────────────── */
194
+ .insight-box {
195
+ background: var(--color-card);
196
+ border: 1px solid var(--color-border);
197
+ /* No border-radius — uses corner-accents class */
198
+ }
199
+
200
+ /* ── Section Textures ────────────────────────────────── */
201
+ /* Apply to <section>: combine one pattern + noise, e.g. class="texture-dots texture-noise" */
202
+ /* Textures should be felt, not seen — low opacity, faded edges */
203
+
204
+ /* ── Fade-In ─────────────────────────────────────────── */
205
+ .fade-in {
206
+ opacity: 0;
207
+ transform: translateY(20px);
208
+ transition: opacity 0.7s ease-out, transform 0.7s ease-out;
209
+ }
210
+ .fade-in-visible {
211
+ opacity: 1;
212
+ transform: translateY(0);
213
+ }
214
+ /* Stagger with data-delay attribute: <div class="fade-in" data-delay="100"> */`;
215
+ /**
216
+ * The registered name of the ECharts theme. The skeleton registers the theme under this name, and the
217
+ * bootstrap reads the same name. One source prevents a silent mismatch between the two sites.
218
+ */
219
+ export const ECHARTS_THEME_NAME = "inflexa";
220
+ /**
221
+ * The ECharts theme as a plain object. The renderer serializes this object and registers it, thus the
222
+ * theme never rides a JSON file and no run-time read is necessary. The palette and the axis styles mirror
223
+ * the light design tokens of the page.
224
+ */
225
+ export const ECHARTS_THEME = {
226
+ color: ["#576dea", "#ef4444", "#22c55e", "#a78bfa", "#f59e0b", "#ec4899", "#06b6d4", "#f97316", "#8b5cf6", "#14b8a6"],
227
+ backgroundColor: "transparent",
228
+ textStyle: {
229
+ fontFamily: "'Space Grotesk Variable', system-ui, sans-serif",
230
+ fontSize: 12,
231
+ color: "#64748b",
232
+ },
233
+ title: {
234
+ show: false,
235
+ },
236
+ legend: {
237
+ bottom: 0,
238
+ textStyle: { fontSize: 11, color: "#64748b" },
239
+ itemGap: 16,
240
+ itemWidth: 12,
241
+ itemHeight: 12,
242
+ },
243
+ grid: {
244
+ left: 60,
245
+ right: 24,
246
+ top: 24,
247
+ bottom: 48,
248
+ containLabel: false,
249
+ },
250
+ tooltip: {
251
+ backgroundColor: "#ffffff",
252
+ borderColor: "#e2e8f0",
253
+ textStyle: { color: "#334155", fontSize: 12 },
254
+ extraCssText: "border-radius: 4px; box-shadow: 0 4px 12px rgba(15,23,42,0.1);",
255
+ },
256
+ toolbox: {
257
+ feature: {
258
+ saveAsImage: { title: "Save", pixelRatio: 2 },
259
+ },
260
+ iconStyle: { borderColor: "#94a3b8" },
261
+ right: 16,
262
+ top: 0,
263
+ },
264
+ xAxis: {
265
+ axisLine: { lineStyle: { color: "#e2e8f0" } },
266
+ axisTick: { lineStyle: { color: "#e2e8f0" } },
267
+ axisLabel: { color: "#64748b", fontSize: 11 },
268
+ splitLine: { lineStyle: { color: "#f1f5f9", type: "dashed" } },
269
+ },
270
+ yAxis: {
271
+ axisLine: { show: false },
272
+ axisTick: { show: false },
273
+ axisLabel: { color: "#64748b", fontSize: 11 },
274
+ splitLine: { lineStyle: { color: "#f1f5f9", type: "dashed" } },
275
+ },
276
+ };
277
+ /**
278
+ * The page-side script that wires each chart. It finds every chart container, reads the option JSON from
279
+ * the sibling `<script type="application/json">` element, and initializes ECharts with the registered
280
+ * theme. The skeleton registers the theme before this script runs. A resize handler keeps each chart
281
+ * fit to the window.
282
+ */
283
+ export const CHART_BOOTSTRAP = `(function () {
284
+ if (typeof echarts === "undefined") {
285
+ return;
286
+ }
287
+ var containers = document.querySelectorAll("[data-echarts-id]");
288
+ for (var i = 0; i < containers.length; i++) {
289
+ var container = containers[i];
290
+ var optionScript = container.nextElementSibling;
291
+ if (!optionScript || optionScript.getAttribute("type") !== "application/json") {
292
+ continue;
293
+ }
294
+ var option = JSON.parse(optionScript.textContent || "{}");
295
+ var chart = echarts.init(container, ${JSON.stringify(ECHARTS_THEME_NAME)});
296
+ chart.setOption(option);
297
+ }
298
+ window.addEventListener("resize", function () {
299
+ var nodes = document.querySelectorAll("[data-echarts-id]");
300
+ for (var j = 0; j < nodes.length; j++) {
301
+ var instance = echarts.getInstanceByDom(nodes[j]);
302
+ if (instance) {
303
+ instance.resize();
304
+ }
305
+ }
306
+ });
307
+ })();`;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The reference ledger state.
3
+ *
4
+ * A claim binds to evidence, and a citation block points at an external source. The ledger collects each
5
+ * reference in first-appearance order, and it gives one marker number to each distinct reference. A claim
6
+ * marker and a citation marker read from the same ledger, thus one reference that two blocks share gets
7
+ * one number and one list entry.
8
+ *
9
+ * Identity is the canonical serialization of the reference. `serializeReference` sorts the keys at every
10
+ * depth, thus two references match only when every field matches. The key order of the source object does
11
+ * not change the identity.
12
+ */
13
+ import { type Reference } from "../contracts/report-reference.js";
14
+ /**
15
+ * The mutable ledger of references.
16
+ *
17
+ * The page walk makes one ledger, and it threads the ledger through each claim and each citation. The
18
+ * order of the entries is the order of the first mark, thus the marker numbers count up by first
19
+ * appearance.
20
+ */
21
+ export declare class ReferenceLedger {
22
+ private readonly order;
23
+ private readonly markers;
24
+ /**
25
+ * Give the marker number of a reference. A new reference gets the next number, and it joins the
26
+ * order. A reference that matches an earlier one gives the earlier number, thus the list holds it one
27
+ * time.
28
+ */
29
+ mark(reference: Reference): number;
30
+ /** The references in first-appearance order. The marker of an entry is its position plus one. */
31
+ entries(): readonly Reference[];
32
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The reference ledger state.
3
+ *
4
+ * A claim binds to evidence, and a citation block points at an external source. The ledger collects each
5
+ * reference in first-appearance order, and it gives one marker number to each distinct reference. A claim
6
+ * marker and a citation marker read from the same ledger, thus one reference that two blocks share gets
7
+ * one number and one list entry.
8
+ *
9
+ * Identity is the canonical serialization of the reference. `serializeReference` sorts the keys at every
10
+ * depth, thus two references match only when every field matches. The key order of the source object does
11
+ * not change the identity.
12
+ */
13
+ import { serializeReference } from "../contracts/report-reference.js";
14
+ /**
15
+ * The mutable ledger of references.
16
+ *
17
+ * The page walk makes one ledger, and it threads the ledger through each claim and each citation. The
18
+ * order of the entries is the order of the first mark, thus the marker numbers count up by first
19
+ * appearance.
20
+ */
21
+ export class ReferenceLedger {
22
+ order = [];
23
+ markers = new Map();
24
+ /**
25
+ * Give the marker number of a reference. A new reference gets the next number, and it joins the
26
+ * order. A reference that matches an earlier one gives the earlier number, thus the list holds it one
27
+ * time.
28
+ */
29
+ mark(reference) {
30
+ const key = serializeReference(reference);
31
+ const seen = this.markers.get(key);
32
+ if (seen !== undefined) {
33
+ return seen;
34
+ }
35
+ const marker = this.order.length + 1;
36
+ this.markers.set(key, marker);
37
+ this.order.push(reference);
38
+ return marker;
39
+ }
40
+ /** The references in first-appearance order. The marker of an entry is its position plus one. */
41
+ entries() {
42
+ return this.order;
43
+ }
44
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The page walk: the document walk, the value-map validation, and the problem collection.
3
+ *
4
+ * `renderReportPage` walks the sections in document order. It dispatches each block to its kind renderer,
5
+ * and it validates the bound value against the shape that the block needs. A missing entry, or an entry of
6
+ * the wrong shape, becomes one typed problem. The walk collects every problem, thus one render reports each
7
+ * fault at one time. A page with no problem assembles to one HTML string.
8
+ *
9
+ * One shared `ReferenceLedger` threads through the whole walk. Thus a claim marker and a citation marker
10
+ * count by first appearance across the page, and a reference that two blocks share keeps one number and one
11
+ * list entry.
12
+ *
13
+ * The output is a pure function of the two inputs. The renderer reads no clock, no random value, and no
14
+ * locale. Thus the same document and the same values give the same bytes.
15
+ */
16
+ import { type Result } from "neverthrow";
17
+ import type { ReportDocument } from "../contracts/report-blocks.js";
18
+ import type { RenderProblem, RenderValues } from "./types.js";
19
+ /**
20
+ * Render a report document and its value map to one HTML string.
21
+ *
22
+ * The walk collects every value problem. When any problem exists, the render returns the problems and no
23
+ * HTML. When no problem exists, the render assembles the page and returns the string.
24
+ */
25
+ export declare function renderReportPage(document: ReportDocument, values: RenderValues): Result<string, RenderProblem[]>;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The page walk: the document walk, the value-map validation, and the problem collection.
3
+ *
4
+ * `renderReportPage` walks the sections in document order. It dispatches each block to its kind renderer,
5
+ * and it validates the bound value against the shape that the block needs. A missing entry, or an entry of
6
+ * the wrong shape, becomes one typed problem. The walk collects every problem, thus one render reports each
7
+ * fault at one time. A page with no problem assembles to one HTML string.
8
+ *
9
+ * One shared `ReferenceLedger` threads through the whole walk. Thus a claim marker and a citation marker
10
+ * count by first appearance across the page, and a reference that two blocks share keeps one number and one
11
+ * list entry.
12
+ *
13
+ * The output is a pure function of the two inputs. The renderer reads no clock, no random value, and no
14
+ * locale. Thus the same document and the same values give the same bytes.
15
+ */
16
+ import { err, ok } from "neverthrow";
17
+ import { renderChart } from "./views/chart-view.js";
18
+ import { deriveChartOption } from "./chart.js";
19
+ import { assemblePage, renderReferenceSection } from "./views/page-view.js";
20
+ import { renderClaim, renderNav, renderSection, renderText } from "./views/prose.js";
21
+ import { ReferenceLedger } from "./references.js";
22
+ import { renderCitation, renderFigure, renderMetric, renderTable } from "./views/values.js";
23
+ /**
24
+ * Render a report document and its value map to one HTML string.
25
+ *
26
+ * The walk collects every value problem. When any problem exists, the render returns the problems and no
27
+ * HTML. When no problem exists, the render assembles the page and returns the string.
28
+ */
29
+ export function renderReportPage(document, values) {
30
+ const problems = [];
31
+ const ledger = new ReferenceLedger();
32
+ const content = [];
33
+ for (const section of document.sections) {
34
+ content.push(renderBlock(section, values, ledger, 0, problems));
35
+ }
36
+ if (problems.length > 0) {
37
+ return err(problems);
38
+ }
39
+ const nav = renderNav(document.sections);
40
+ const references = renderReferenceSection(ledger);
41
+ return ok(assemblePage(document.title, nav, content.join(""), references));
42
+ }
43
+ /**
44
+ * Render one block to its HTML, and collect a value problem into `problems`. A block whose value is missing
45
+ * or of the wrong shape adds one problem and renders nothing. The empty result drops out, because the whole
46
+ * render fails when any problem exists.
47
+ */
48
+ function renderBlock(block, values, ledger, depth, problems) {
49
+ switch (block.kind) {
50
+ case "text":
51
+ return renderText(block);
52
+ case "claim":
53
+ return renderClaim(block, ledger);
54
+ case "citation":
55
+ return renderCitation(block, ledger);
56
+ case "metric": {
57
+ const entry = values[block.id];
58
+ if (entry === undefined) {
59
+ problems.push(missingValue(block.id, "scalar"));
60
+ return "";
61
+ }
62
+ if (entry.type !== "scalar") {
63
+ problems.push(wrongShape(block.id, entry.type, "scalar"));
64
+ return "";
65
+ }
66
+ return renderMetric(block, entry);
67
+ }
68
+ case "table": {
69
+ const entry = values[block.id];
70
+ if (entry === undefined) {
71
+ problems.push(missingValue(block.id, "table"));
72
+ return "";
73
+ }
74
+ if (entry.type !== "table") {
75
+ problems.push(wrongShape(block.id, entry.type, "table"));
76
+ return "";
77
+ }
78
+ return renderTable(block, entry);
79
+ }
80
+ case "figure": {
81
+ const entry = values[block.id];
82
+ if (entry === undefined) {
83
+ problems.push(missingValue(block.id, "figure"));
84
+ return "";
85
+ }
86
+ if (entry.type !== "figure") {
87
+ problems.push(wrongShape(block.id, entry.type, "figure"));
88
+ return "";
89
+ }
90
+ return renderFigure(block, entry);
91
+ }
92
+ case "chart": {
93
+ const entry = values[block.id];
94
+ if (entry === undefined) {
95
+ problems.push(missingValue(block.id, "table"));
96
+ return "";
97
+ }
98
+ if (entry.type !== "table") {
99
+ problems.push(wrongShape(block.id, entry.type, "table"));
100
+ return "";
101
+ }
102
+ const option = deriveChartOption(block, entry.rows, entry.columns);
103
+ if (option.isErr()) {
104
+ problems.push(option.error);
105
+ return "";
106
+ }
107
+ return renderChart(block, option.value);
108
+ }
109
+ case "section": {
110
+ const parts = [];
111
+ for (const child of block.blocks) {
112
+ parts.push(renderBlock(child, values, ledger, depth + 1, problems));
113
+ }
114
+ return renderSection(block, depth, parts.join(""));
115
+ }
116
+ }
117
+ }
118
+ /** A `missing-value` problem that names the block and the shape that it needs. */
119
+ function missingValue(blockId, expected) {
120
+ return { blockId, kind: "missing-value", detail: `The block needs a ${expected} value entry.` };
121
+ }
122
+ /** A `wrong-shape` problem that names the block, the shape that arrived, and the shape that it needs. */
123
+ function wrongShape(blockId, actual, expected) {
124
+ return { blockId, kind: "wrong-shape", detail: `The block needs a ${expected} value entry, but the entry is a ${actual}.` };
125
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The one script sink helper.
3
+ *
4
+ * The markup runtime escapes each child and each attribute value. A `<script>` sink is the exception,
5
+ * because the JSON inside it goes to the page through `raw()`, and `raw()` adds no protection. Thus this
6
+ * helper is the sole guard of the two JSON sinks.
7
+ *
8
+ * A `</script` sequence inside a data cell can close the element too soon. The helper serializes the value,
9
+ * then it replaces every `<` with `\u003c`. The replacement makes a `</script` sequence unrepresentable,
10
+ * and the JSON value stays identical, because the JSON parser reads `\u003c` as `<`.
11
+ */
12
+ /** Serialize a value to JSON that is safe inside a `<script>` element. */
13
+ export declare function scriptJson(value: unknown): string;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The one script sink helper.
3
+ *
4
+ * The markup runtime escapes each child and each attribute value. A `<script>` sink is the exception,
5
+ * because the JSON inside it goes to the page through `raw()`, and `raw()` adds no protection. Thus this
6
+ * helper is the sole guard of the two JSON sinks.
7
+ *
8
+ * A `</script` sequence inside a data cell can close the element too soon. The helper serializes the value,
9
+ * then it replaces every `<` with `\u003c`. The replacement makes a `</script` sequence unrepresentable,
10
+ * and the JSON value stays identical, because the JSON parser reads `\u003c` as `<`.
11
+ */
12
+ /** Serialize a value to JSON that is safe inside a `<script>` element. */
13
+ export function scriptJson(value) {
14
+ return JSON.stringify(value).replace(/</g, "\\u003c");
15
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The value model that the renderer takes, keyed by block id.
3
+ *
4
+ * The renderer declares its own value union. It does not take `ResolvedValue`, because a `ResolvedValue`
5
+ * can be a file echo of a path and a hash. A page cannot show a path and a hash. The caller owns the
6
+ * policy that turns the echo into a source string. The string is a data URI, or a URL that the host
7
+ * serves. Thus a `figure` here carries a ready `src` string, and the renderer holds no policy about the
8
+ * image bytes.
9
+ */
10
+ /**
11
+ * The value that one block resolves to, as a closed union. A `scalar` gives one number or one string. A
12
+ * `table` gives the rows and the optional column order. A `figure` gives a ready source string. A
13
+ * `citation` echoes one external id.
14
+ */
15
+ export type RenderValue = {
16
+ type: "scalar";
17
+ value: string | number;
18
+ } | {
19
+ type: "table";
20
+ rows: Array<Record<string, string | number>>;
21
+ columns?: string[];
22
+ } | {
23
+ type: "figure";
24
+ src: string;
25
+ } | {
26
+ type: "citation";
27
+ id: string;
28
+ };
29
+ /**
30
+ * One typed fault of a render. `blockId` names the block. `kind` names the class of the fault. `detail`
31
+ * names what the block expected. The render collects every problem, and it returns them at once.
32
+ */
33
+ export interface RenderProblem {
34
+ blockId: string;
35
+ kind: "missing-value" | "wrong-shape" | "invalid-chart-input";
36
+ detail: string;
37
+ }
38
+ /** The value map that the renderer takes, keyed by block id. */
39
+ export type RenderValues = Record<string, RenderValue>;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The value model that the renderer takes, keyed by block id.
3
+ *
4
+ * The renderer declares its own value union. It does not take `ResolvedValue`, because a `ResolvedValue`
5
+ * can be a file echo of a path and a hash. A page cannot show a path and a hash. The caller owns the
6
+ * policy that turns the echo into a source string. The string is a data URI, or a URL that the host
7
+ * serves. Thus a `figure` here carries a ready `src` string, and the renderer holds no policy about the
8
+ * image bytes.
9
+ */
10
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The chart container markup.
3
+ *
4
+ * The bootstrap in `page.ts` reads the option from the next element sibling of the container. Thus the
5
+ * `<script type="application/json">` element MUST follow the container div with no element between them.
6
+ * The title renders above the container, and the caption renders below.
7
+ */
8
+ import type { ChartBlock } from "../../contracts/report-blocks.js";
9
+ import type { EchartOption } from "../chart.js";
10
+ /** Render the container markup for a chart. */
11
+ export declare function renderChart(block: ChartBlock, option: EchartOption): string;
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
+ /**
3
+ * The chart container markup.
4
+ *
5
+ * The bootstrap in `page.ts` reads the option from the next element sibling of the container. Thus the
6
+ * `<script type="application/json">` element MUST follow the container div with no element between them.
7
+ * The title renders above the container, and the caption renders below.
8
+ */
9
+ import { raw } from "hono/html";
10
+ import { scriptJson } from "../script-json.js";
11
+ /** Render the container markup for a chart. */
12
+ export function renderChart(block, option) {
13
+ const containerId = chartContainerId(block.id);
14
+ // The JSON goes to the page through `raw()`, thus `scriptJson` is the sole guard of this sink. It
15
+ // replaces every `<` with `\u003c`, thus a `</script` sequence in a string cell cannot close the
16
+ // element early. The JSON parser reads `\u003c` as `<`, thus the option value stays exact.
17
+ const json = scriptJson(option);
18
+ return String(_jsxs(_Fragment, { children: [block.title !== undefined ? _jsx("p", { class: "chart-title", children: block.title }) : null, _jsx("div", { id: containerId, "data-echarts-id": block.id, class: "chart-container" }), _jsx("script", { type: "application/json", children: raw(json) }), block.caption !== undefined ? _jsx("p", { class: "chart-caption", children: block.caption }) : null] }));
19
+ }
20
+ /** The container id derives from the block id, thus the id stays stable across renders. */
21
+ function chartContainerId(blockId) {
22
+ return `chart-${blockId}`;
23
+ }