@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,20 @@
1
+ /**
2
+ * The page assembly.
3
+ *
4
+ * The skeleton inlines the style rules in the head, and it puts the two scripts at the end of the body.
5
+ * The theme registration runs before the chart bootstrap, thus each chart finds its theme. The
6
+ * navigation, the main content, and the reference frame arrive as already-escaped markup strings, thus
7
+ * `raw()` inserts them byte for byte.
8
+ */
9
+ import type { ReferenceLedger } from "../references.js";
10
+ /**
11
+ * Wrap the reference list in one fixed frame. An empty ledger gives an empty string, thus the page shows
12
+ * no empty reference frame.
13
+ */
14
+ export declare function renderReferenceSection(ledger: ReferenceLedger): string;
15
+ /**
16
+ * Assemble the page from the title, the navigation, the main content, and the reference frame. The title
17
+ * passes through the runtime as text, and the runtime escapes it. The reference frame is optional, thus
18
+ * an empty frame adds no markup.
19
+ */
20
+ export declare function assemblePage(title: string, nav: string, content: string, references: string): string;
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
+ /**
3
+ * The page assembly.
4
+ *
5
+ * The skeleton inlines the style rules in the head, and it puts the two scripts at the end of the body.
6
+ * The theme registration runs before the chart bootstrap, thus each chart finds its theme. The
7
+ * navigation, the main content, and the reference frame arrive as already-escaped markup strings, thus
8
+ * `raw()` inserts them byte for byte.
9
+ */
10
+ import { raw } from "hono/html";
11
+ import { CDN_HEAD, CHART_BOOTSTRAP, ECHARTS_THEME, ECHARTS_THEME_NAME, PAGE_CSS } from "../page.js";
12
+ import { renderReferenceList } from "./references-view.js";
13
+ import { scriptJson } from "../script-json.js";
14
+ /**
15
+ * The theme object as script-safe JSON. `scriptJson` replaces every `<` with `\u003c`, thus a later edit
16
+ * that adds a `<` to the theme stays safe inside the script sink. The JSON parser reads `\u003c` as `<`, thus
17
+ * the theme value stays exact.
18
+ */
19
+ const THEME_JSON = scriptJson(ECHARTS_THEME);
20
+ /**
21
+ * The registration script for the ECharts theme. It runs before the chart bootstrap, thus each chart
22
+ * reads the registered theme by its name. The guard skips the call when the ECharts runtime did not load.
23
+ */
24
+ const THEME_REGISTRATION = `(function () {
25
+ if (typeof echarts === "undefined") {
26
+ return;
27
+ }
28
+ echarts.registerTheme(${JSON.stringify(ECHARTS_THEME_NAME)}, ${THEME_JSON});
29
+ })();`;
30
+ /**
31
+ * Wrap the reference list in one fixed frame. An empty ledger gives an empty string, thus the page shows
32
+ * no empty reference frame.
33
+ */
34
+ export function renderReferenceSection(ledger) {
35
+ const list = renderReferenceList(ledger);
36
+ if (list === "") {
37
+ return "";
38
+ }
39
+ return String(_jsxs("section", { class: "report-references-section mt-16 pt-8 border-t border-slate-200", children: [_jsx("h2", { class: "text-xl font-semibold tracking-tight text-slate-900 mb-4", children: "References" }), raw(list)] }));
40
+ }
41
+ /**
42
+ * Assemble the page from the title, the navigation, the main content, and the reference frame. The title
43
+ * passes through the runtime as text, and the runtime escapes it. The reference frame is optional, thus
44
+ * an empty frame adds no markup.
45
+ */
46
+ export function assemblePage(title, nav, content, references) {
47
+ return ("<!doctype html>" +
48
+ String(_jsxs("html", { lang: "en", children: [_jsxs("head", { children: [_jsx("meta", { charset: "utf-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("title", { children: title }), raw(CDN_HEAD), _jsx("style", { children: raw(PAGE_CSS) })] }), _jsxs("body", { children: [raw(nav), _jsxs("main", { class: "report-main mx-auto max-w-4xl px-6 py-10", children: [_jsx("h1", { class: "report-title text-4xl font-bold tracking-tight text-slate-900 mb-8", children: title }), raw(content), references.length > 0 ? raw(references) : null] }), _jsx("script", { children: raw(THEME_REGISTRATION) }), _jsx("script", { children: raw(CHART_BOOTSTRAP) })] })] })));
49
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The prose markup: the text block, the claim block, the section wrapper, and the page navigation.
3
+ *
4
+ * The runtime escapes every interpolated string, thus hostile prose reaches the page as text. A text
5
+ * block and a claim block split the prose on a blank line, and each part becomes one paragraph. A claim
6
+ * adds one marker for each binding, and the markers number through the shared ledger.
7
+ */
8
+ import type { ClaimBlock, SectionBlock, TextBlock } from "../../contracts/report-blocks.js";
9
+ import type { ReferenceLedger } from "../references.js";
10
+ /** Render a text block as escaped paragraphs. */
11
+ export declare function renderText(block: TextBlock): string;
12
+ /**
13
+ * Render a claim block as escaped paragraphs plus evidence markers. The markers attach to the last
14
+ * paragraph, thus they sit at the end of the claim text. The ledger marks each binding, thus a shared
15
+ * reference keeps one number across the page.
16
+ */
17
+ export declare function renderClaim(block: ClaimBlock, ledger: ReferenceLedger): string;
18
+ /**
19
+ * Render a section wrapper around its rendered children. The heading level is `h2` at depth 0, `h3` at
20
+ * depth 1, and `h4` at depth 2 or deeper. The walk renders the children first, and it passes the markup
21
+ * as one already-escaped string, thus `raw()` inserts it byte for byte.
22
+ */
23
+ export declare function renderSection(section: SectionBlock, depth: number, childrenHtml: string): string;
24
+ /**
25
+ * Render the fixed left navigation. Each anchor targets one top-level section by its block id, with a
26
+ * numeric badge for its position. The brand row stays, and the mobile toggle and the icon slot drop out.
27
+ */
28
+ export declare function renderNav(topSections: SectionBlock[]): string;
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
+ /**
3
+ * The prose markup: the text block, the claim block, the section wrapper, and the page navigation.
4
+ *
5
+ * The runtime escapes every interpolated string, thus hostile prose reaches the page as text. A text
6
+ * block and a claim block split the prose on a blank line, and each part becomes one paragraph. A claim
7
+ * adds one marker for each binding, and the markers number through the shared ledger.
8
+ */
9
+ import { raw } from "hono/html";
10
+ import { Marker } from "./references-view.js";
11
+ /** The paragraph class of a text block and a claim block. */
12
+ const PARAGRAPH_CLASS = "report-paragraph mb-4 leading-relaxed text-slate-600";
13
+ /**
14
+ * Split prose into paragraphs on a blank line. A run of blank lines makes one split. An empty part drops
15
+ * out, thus a leading or a trailing blank line adds no empty paragraph.
16
+ */
17
+ function splitParagraphs(prose) {
18
+ const parts = prose.split(/\n[ \t]*\n/);
19
+ const paragraphs = [];
20
+ for (const part of parts) {
21
+ const trimmed = part.trim();
22
+ if (trimmed.length > 0) {
23
+ paragraphs.push(trimmed);
24
+ }
25
+ }
26
+ return paragraphs;
27
+ }
28
+ /** Render a text block as escaped paragraphs. */
29
+ export function renderText(block) {
30
+ const paragraphs = splitParagraphs(block.content.prose);
31
+ return String(_jsx(_Fragment, { children: paragraphs.map((paragraph) => (_jsx("p", { class: PARAGRAPH_CLASS, children: paragraph }))) }));
32
+ }
33
+ /**
34
+ * Render a claim block as escaped paragraphs plus evidence markers. The markers attach to the last
35
+ * paragraph, thus they sit at the end of the claim text. The ledger marks each binding, thus a shared
36
+ * reference keeps one number across the page.
37
+ */
38
+ export function renderClaim(block, ledger) {
39
+ const markerNumbers = block.bindings.map((reference) => ledger.mark(reference));
40
+ const markers = markerNumbers.map((n) => _jsx(Marker, { n: n }));
41
+ const paragraphs = splitParagraphs(block.content.prose);
42
+ if (paragraphs.length === 0) {
43
+ return String(_jsx("p", { class: PARAGRAPH_CLASS, children: markers }));
44
+ }
45
+ const last = paragraphs.length - 1;
46
+ return String(_jsx(_Fragment, { children: paragraphs.map((paragraph, index) => (_jsxs("p", { class: PARAGRAPH_CLASS, children: [paragraph, index === last ? markers : null] }))) }));
47
+ }
48
+ /** The heading class of a section, by heading level. */
49
+ function headingClass(level) {
50
+ switch (level) {
51
+ case 2:
52
+ return "text-3xl font-semibold tracking-tight text-slate-900 mb-4";
53
+ case 3:
54
+ return "text-2xl font-semibold tracking-tight text-slate-900 mb-3";
55
+ default:
56
+ return "text-xl font-semibold tracking-tight text-slate-900 mb-2";
57
+ }
58
+ }
59
+ /**
60
+ * Render a section wrapper around its rendered children. The heading level is `h2` at depth 0, `h3` at
61
+ * depth 1, and `h4` at depth 2 or deeper. The walk renders the children first, and it passes the markup
62
+ * as one already-escaped string, thus `raw()` inserts it byte for byte.
63
+ */
64
+ export function renderSection(section, depth, childrenHtml) {
65
+ const level = Math.min(depth + 2, 4);
66
+ const Heading = `h${level}`;
67
+ return String(_jsxs("section", { id: section.id, class: "report-section mb-10", children: [_jsx(Heading, { class: headingClass(level), children: section.title }), raw(childrenHtml)] }));
68
+ }
69
+ /**
70
+ * Render the fixed left navigation. Each anchor targets one top-level section by its block id, with a
71
+ * numeric badge for its position. The brand row stays, and the mobile toggle and the icon slot drop out.
72
+ */
73
+ export function renderNav(topSections) {
74
+ return String(_jsxs("aside", { id: "report-sidebar", class: "fixed top-0 left-0 z-40 h-screen w-60 flex flex-col border-r border-slate-200 bg-white/95 backdrop-blur", "aria-label": "Report navigation", children: [_jsx("div", { class: "flex items-center gap-2 px-5 py-5 border-b border-slate-200", children: _jsx("span", { class: "font-mono text-sm font-semibold tracking-wider text-primary-500", children: "inflexa" }) }), _jsx("nav", { class: "flex-1 overflow-y-auto py-3", "aria-label": "Report sections", children: topSections.map((section, index) => (_jsxs("a", { href: `#${section.id}`, "data-sidebar-link": "", class: "sidebar-link flex items-center gap-3 px-5 py-2 text-sm text-slate-500 hover:text-slate-900 hover:bg-slate-50 transition-colors", children: [_jsx("span", { class: "font-mono text-[10px] font-semibold tracking-wider text-slate-300 shrink-0 w-4 text-right", children: index + 1 }), _jsx("span", { class: "truncate", children: section.title })] }))) })] }));
75
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The reference markup: the evidence marker and the ordered reference list.
3
+ *
4
+ * The runtime escapes each child and each attribute value, thus a hostile path, a hostile id, or a
5
+ * hostile operation reaches the page as text. The list keeps the first-appearance order of the ledger,
6
+ * thus the ordinal of each item matches its marker.
7
+ */
8
+ import type { ReferenceLedger } from "../references.js";
9
+ /**
10
+ * One evidence marker as a superscript that links to the list entry. The number comes from the ledger as
11
+ * an integer.
12
+ */
13
+ export declare function Marker({ n }: {
14
+ n: number;
15
+ }): import("hono/jsx/jsx-dev-runtime").JSX.Element;
16
+ /**
17
+ * The ordered reference list for the end of the page. An empty ledger gives an empty string, thus the
18
+ * page shows no empty list.
19
+ */
20
+ export declare function renderReferenceList(ledger: ReferenceLedger): string;
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "hono/jsx/jsx-runtime";
2
+ /**
3
+ * One evidence marker as a superscript that links to the list entry. The number comes from the ledger as
4
+ * an integer.
5
+ */
6
+ export function Marker({ n }) {
7
+ return (_jsx("sup", { class: "report-marker font-mono text-[10px] text-primary-500", children: _jsx("a", { href: `#ref-${n}`, children: n }) }));
8
+ }
9
+ /** The kind label of one reference entry. */
10
+ function RefKind({ children }) {
11
+ return _jsx("span", { class: "report-ref-kind font-mono text-[11px] uppercase tracking-wider text-slate-400", children: children });
12
+ }
13
+ /** A path in a monospace code span. */
14
+ function PathCode({ children }) {
15
+ return _jsx("code", { class: "font-mono text-primary-700", children: children });
16
+ }
17
+ /** A plain detail such as a locator or an external id. */
18
+ function Detail({ children }) {
19
+ return _jsx("span", { class: "text-slate-500", children: children });
20
+ }
21
+ /**
22
+ * Describe a locator as plain text: the column and the one row selector. The refine of the schema admits
23
+ * exactly one of `row` or `rowFilter`, thus the two branches cover a valid locator.
24
+ */
25
+ function describeLocator(locator) {
26
+ if (locator.row !== undefined) {
27
+ return `column ${locator.column}, row ${locator.row}`;
28
+ }
29
+ const filter = locator.rowFilter;
30
+ if (filter !== undefined) {
31
+ return `column ${locator.column}, row where ${filter.column} ${filter.op} ${String(filter.value)}`;
32
+ }
33
+ return `column ${locator.column}`;
34
+ }
35
+ /**
36
+ * The inner markup of one list entry. An artifact entry names its path, and it adds the locator when the
37
+ * reference pins one cell. A citation entry names its identifier space and its id. A derivation entry
38
+ * names its operation and its two inputs, each by path and locator.
39
+ */
40
+ function ReferenceEntry({ reference }) {
41
+ switch (reference.kind) {
42
+ case "artifact-value":
43
+ return (_jsxs(_Fragment, { children: [_jsx(RefKind, { children: "Artifact value" }), " ", _jsx(PathCode, { children: reference.path }), " ", _jsx(Detail, { children: describeLocator(reference.locator) })] }));
44
+ case "artifact-table":
45
+ return (_jsxs(_Fragment, { children: [_jsx(RefKind, { children: "Artifact table" }), " ", _jsx(PathCode, { children: reference.path })] }));
46
+ case "artifact-file":
47
+ return (_jsxs(_Fragment, { children: [_jsx(RefKind, { children: "Artifact file" }), " ", _jsx(PathCode, { children: reference.path })] }));
48
+ case "citation":
49
+ return (_jsxs(_Fragment, { children: [_jsx(RefKind, { children: "Citation" }), " ", _jsx(Detail, { children: `${reference.idKind}:${reference.id}` })] }));
50
+ case "derivation":
51
+ return (_jsxs(_Fragment, { children: [_jsx(RefKind, { children: `Derivation (${reference.op})` }), " ", reference.inputs.map((input, index) => (_jsxs(_Fragment, { children: [index > 0 ? " and " : "", _jsx(PathCode, { children: input.path }), " ", _jsx(Detail, { children: describeLocator(input.locator) })] })))] }));
52
+ }
53
+ }
54
+ /**
55
+ * The ordered reference list for the end of the page. An empty ledger gives an empty string, thus the
56
+ * page shows no empty list.
57
+ */
58
+ export function renderReferenceList(ledger) {
59
+ const entries = ledger.entries();
60
+ if (entries.length === 0) {
61
+ return "";
62
+ }
63
+ return String(_jsx("ol", { class: "report-references list-decimal pl-6", children: entries.map((reference, index) => (_jsx("li", { id: `ref-${index + 1}`, class: "report-ref-item mb-2 text-sm text-slate-600", children: _jsx(ReferenceEntry, { reference: reference }) }))) }));
64
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The value markup: the metric card, the data table, the figure, and the inline citation.
3
+ *
4
+ * The caller resolves each value, and it narrows the value to the shape that the block needs. The runtime
5
+ * escapes every interpolated string, thus a hostile label, a hostile cell, a hostile caption, or a
6
+ * hostile source reaches the page as text.
7
+ */
8
+ import type { CitationBlock, FigureBlock, MetricBlock, TableBlock } from "../../contracts/report-blocks.js";
9
+ import type { ReferenceLedger } from "../references.js";
10
+ import type { RenderValue } from "../types.js";
11
+ /** The scalar value that a metric renders from. */
12
+ type ScalarValue = Extract<RenderValue, {
13
+ type: "scalar";
14
+ }>;
15
+ /** The table value that a table renders from. */
16
+ type TableValue = Extract<RenderValue, {
17
+ type: "table";
18
+ }>;
19
+ /** The figure value that a figure renders from. */
20
+ type FigureValue = Extract<RenderValue, {
21
+ type: "figure";
22
+ }>;
23
+ /** Render a metric block as a stat card with the label and the scalar value. */
24
+ export declare function renderMetric(block: MetricBlock, value: ScalarValue): string;
25
+ /**
26
+ * Render a table block. The header holds one cell for each column, and the body holds one row for each
27
+ * resolved row. A zero-row table renders the header alone. The title and the caption render only when the
28
+ * block carries one. An absent cell renders as an empty cell, thus a ragged row keeps its shape.
29
+ */
30
+ export declare function renderTable(block: TableBlock, value: TableValue): string;
31
+ /**
32
+ * Render a figure block. The source rides the `src` attribute, and the caption renders below the image.
33
+ * The caption also fills the `alt` attribute, thus the escape keeps a hostile source and a hostile caption
34
+ * inside their slots.
35
+ */
36
+ export declare function renderFigure(block: FigureBlock, value: FigureValue): string;
37
+ /**
38
+ * Render a citation block as a short line. The binding joins the ledger like a claim binding, thus one
39
+ * shared source keeps one number. The optional note renders after the marker.
40
+ */
41
+ export declare function renderCitation(block: CitationBlock, ledger: ReferenceLedger): string;
42
+ export {};
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "hono/jsx/jsx-runtime";
2
+ import { Marker } from "./references-view.js";
3
+ /** Render a metric block as a stat card with the label and the scalar value. */
4
+ export function renderMetric(block, value) {
5
+ return String(_jsxs("div", { class: "corner-accents border border-slate-200 bg-white p-6", children: [_jsx("div", { class: "font-mono text-3xl font-bold", style: "color: #576dea;", children: value.value }), _jsx("div", { class: "font-mono text-xs font-medium text-slate-500 mt-2 uppercase tracking-wider", children: block.label })] }));
6
+ }
7
+ /**
8
+ * The column order of a table. The value columns win when they are present. The keys of the first row
9
+ * give the order otherwise, in first-appearance order.
10
+ */
11
+ function tableColumns(value) {
12
+ if (value.columns !== undefined) {
13
+ return value.columns;
14
+ }
15
+ const first = value.rows[0];
16
+ return first !== undefined ? Object.keys(first) : [];
17
+ }
18
+ /**
19
+ * Render a table block. The header holds one cell for each column, and the body holds one row for each
20
+ * resolved row. A zero-row table renders the header alone. The title and the caption render only when the
21
+ * block carries one. An absent cell renders as an empty cell, thus a ragged row keeps its shape.
22
+ */
23
+ export function renderTable(block, value) {
24
+ const columns = tableColumns(value);
25
+ return String(_jsxs("div", { class: "report-table mb-8", children: [block.title !== undefined ? (_jsx("div", { class: "font-mono text-xs font-semibold uppercase tracking-widest text-primary-500 mb-3", children: block.title })) : null, _jsx("div", { class: "corner-accents border border-slate-200 bg-white overflow-hidden", children: _jsx("div", { class: "overflow-x-auto", children: _jsxs("table", { class: "w-full text-sm", children: [_jsx("thead", { children: _jsx("tr", { class: "border-b border-slate-200 bg-slate-50", children: columns.map((column) => (_jsx("th", { class: "px-4 py-3 text-left font-mono text-[11px] font-semibold text-slate-500 uppercase tracking-wider", children: column }))) }) }), _jsx("tbody", { children: value.rows.map((row) => (_jsx("tr", { class: "report-row border-b border-slate-100 last:border-b-0", children: columns.map((column) => (_jsx("td", { class: "px-4 py-2.5 text-slate-700", children: row[column] }))) }))) })] }) }) }), block.caption !== undefined ? _jsx("p", { class: "text-sm text-slate-400 mt-2", children: block.caption }) : null] }));
26
+ }
27
+ /**
28
+ * Render a figure block. The source rides the `src` attribute, and the caption renders below the image.
29
+ * The caption also fills the `alt` attribute, thus the escape keeps a hostile source and a hostile caption
30
+ * inside their slots.
31
+ */
32
+ export function renderFigure(block, value) {
33
+ const caption = block.caption;
34
+ return String(_jsxs("figure", { class: "report-figure mb-8", children: [_jsx("img", { src: value.src, alt: caption !== undefined ? caption : "", class: "max-w-full h-auto border border-slate-200" }), caption !== undefined ? _jsx("figcaption", { class: "text-sm text-slate-400 mt-2", children: caption }) : null] }));
35
+ }
36
+ /**
37
+ * Render a citation block as a short line. The binding joins the ledger like a claim binding, thus one
38
+ * shared source keeps one number. The optional note renders after the marker.
39
+ */
40
+ export function renderCitation(block, ledger) {
41
+ const n = ledger.mark(block.binding);
42
+ return String(_jsxs("p", { class: "report-citation text-sm text-slate-500", children: [_jsx(Marker, { n: n }), block.note !== undefined ? (_jsxs(_Fragment, { children: [" ", _jsx("span", { class: "text-slate-600", children: block.note })] })) : null] }));
43
+ }
@@ -11,9 +11,9 @@
11
11
  * to a no-op so a library host never acquires process-wide telemetry (or a
12
12
  * console banner) it did not ask for. An embedder that wants harness OTel
13
13
  * passes `initOtel` / `shutdownOtel` here.
14
- * 2. `validateAgentSkills(skillsDir, SANDBOX_AGENT_META)` — pure fs stat, zero
15
- * external deps. A `meta.skills` typo or a `skillsDir` / image drift dies in
16
- * milliseconds, before any Postgres or DBOS cost is paid (agent-skill-assignment).
14
+ * 2. `validateAgentSkills(skillsDir, ...)` — pure fs stat, zero external deps.
15
+ * A `meta.skills` typo or a `skillsDir` / image drift dies in milliseconds,
16
+ * before any Postgres or DBOS cost is paid (agent-skill-assignment).
17
17
  * 3. `initCortexState(pool)` — app tables must exist before launch; recovery
18
18
  * queries them on the first step.
19
19
  * 4. `assertConnectionBudget(...)` — needs the live pool; gates launch so a
@@ -11,9 +11,9 @@
11
11
  * to a no-op so a library host never acquires process-wide telemetry (or a
12
12
  * console banner) it did not ask for. An embedder that wants harness OTel
13
13
  * passes `initOtel` / `shutdownOtel` here.
14
- * 2. `validateAgentSkills(skillsDir, SANDBOX_AGENT_META)` — pure fs stat, zero
15
- * external deps. A `meta.skills` typo or a `skillsDir` / image drift dies in
16
- * milliseconds, before any Postgres or DBOS cost is paid (agent-skill-assignment).
14
+ * 2. `validateAgentSkills(skillsDir, ...)` — pure fs stat, zero external deps.
15
+ * A `meta.skills` typo or a `skillsDir` / image drift dies in milliseconds,
16
+ * before any Postgres or DBOS cost is paid (agent-skill-assignment).
17
17
  * 3. `initCortexState(pool)` — app tables must exist before launch; recovery
18
18
  * queries them on the first step.
19
19
  * 4. `assertConnectionBudget(...)` — needs the live pool; gates launch so a
@@ -38,6 +38,7 @@
38
38
  */
39
39
  import { unwrapOrThrow } from "../lib/result.js";
40
40
  import { SANDBOX_AGENT_META } from "../agents/sandbox/index.js";
41
+ import { REPORT_BUILDER_AGENT_ID, REPORT_BUILDER_SKILLS } from "../agents/report-builder.js";
41
42
  import { validateAgentSkills } from "../agents/sandbox/validate-skills.js";
42
43
  import { initCortexState } from "../state/init.js";
43
44
  import { backfillConversationDisplayEnvelopes } from "../memory/conversation-display-backfill.js";
@@ -48,10 +49,20 @@ import { markDraining } from "./lifecycle.js";
48
49
  import { runShutdownSequence } from "./shutdown.js";
49
50
  const noop = () => { };
50
51
  const noopAsync = () => Promise.resolve();
52
+ /**
53
+ * Every agent whose declared packs must resolve before the first run. The
54
+ * report-builder is not in the sandbox catalog — it is not plannable — but it
55
+ * declares a pack all the same, and a missing one there surfaces as a failed
56
+ * render partway through a report rather than at boot.
57
+ */
58
+ const SKILL_DECLARING_AGENTS = {
59
+ ...SANDBOX_AGENT_META,
60
+ [REPORT_BUILDER_AGENT_ID]: { id: REPORT_BUILDER_AGENT_ID, skills: REPORT_BUILDER_SKILLS },
61
+ };
51
62
  export async function bootHarness(deps) {
52
63
  const { core, pool, skillsDir, logger } = deps;
53
64
  (deps.initTelemetry ?? noop)();
54
- validateAgentSkills(skillsDir, SANDBOX_AGENT_META);
65
+ validateAgentSkills(skillsDir, SKILL_DECLARING_AGENTS);
55
66
  await initCortexState(pool);
56
67
  await assertConnectionBudget({ pool, logger, config: deps.connectionBudget });
57
68
  const runtime = assembleCoreRuntime(core);
@@ -38,7 +38,8 @@ export interface DbosConfig {
38
38
  */
39
39
  readonly logLevel?: string;
40
40
  }
41
- interface State {
41
+ /** DBOS lifecycle facts a host's readiness probe reports on. */
42
+ export interface DbosState {
42
43
  launched: boolean;
43
44
  recoveryStarted: boolean;
44
45
  }
@@ -85,9 +86,8 @@ export declare function sweepEphemeralWorkflows({ pool, logger: injectedLogger,
85
86
  executorId: string;
86
87
  }): Promise<void>;
87
88
  /** Snapshot of DBOS lifecycle state — read by the readiness probe. */
88
- export declare function dbosState(): State;
89
+ export declare function dbosState(): DbosState;
89
90
  /** Test hook: force-reset state without calling DBOS. Test-only. */
90
91
  export declare function __resetDbosStateForTest(): void;
91
92
  /** Test hook: mark launched without calling DBOS. Test-only. */
92
- export declare function __setDbosStateForTest(next: Partial<State>): void;
93
- export {};
93
+ export declare function __setDbosStateForTest(next: Partial<DbosState>): void;
@@ -57,6 +57,22 @@ export interface StepArtifactRef {
57
57
  * slice of it — and so the same call replays to the same rows.
58
58
  */
59
59
  export declare function queryStepArtifactPaths(pool: Querier, resourceId: string, runId: string, stepId: string, limit: number): Promise<StepArtifactRef[]>;
60
+ /** One registered artifact of an analysis, as a reader of the full artifact set names it. */
61
+ export interface AnalysisArtifactRef {
62
+ /** Workspace-root-relative path (`runs/{runId}/{stepId}/output/de.csv`). */
63
+ path: string;
64
+ hash: string;
65
+ fileType: string | null;
66
+ }
67
+ /**
68
+ * Every artifact of one analysis, ordered by path so the same call gives the same rows.
69
+ *
70
+ * The query applies no filter. An input row is a member, the same as a step output. A row that carries
71
+ * `unrecoverable_at` is a member too, because the artifact existed at the moment of this read. If the
72
+ * query drops such a row, a later refusal says that the artifact never existed, and that reason is
73
+ * false.
74
+ */
75
+ export declare function queryAnalysisArtifacts(pool: Querier, analysisId: string): Promise<AnalysisArtifactRef[]>;
60
76
  /** Count step-output artifacts produced by a run — for the run-completed card. */
61
77
  export declare function countArtifactsForRun(pool: Querier, analysisId: string, runId: string): Promise<number>;
62
78
  export declare function updateArtifactId(pool: Querier, resourceId: string, path: string, artifactId: string, fileType?: string | null): Promise<void>;
@@ -148,6 +148,24 @@ export async function queryStepArtifactPaths(pool, resourceId, runId, stepId, li
148
148
  });
149
149
  return result.rows.map((r) => ({ path: r.path, fileType: r.file_type ?? null }));
150
150
  }
151
+ /**
152
+ * Every artifact of one analysis, ordered by path so the same call gives the same rows.
153
+ *
154
+ * The query applies no filter. An input row is a member, the same as a step output. A row that carries
155
+ * `unrecoverable_at` is a member too, because the artifact existed at the moment of this read. If the
156
+ * query drops such a row, a later refusal says that the artifact never existed, and that reason is
157
+ * false.
158
+ */
159
+ export async function queryAnalysisArtifacts(pool, analysisId) {
160
+ const result = await pool.query({
161
+ text: `SELECT path, hash, file_type
162
+ FROM cortex_artifacts
163
+ WHERE analysis_id = $1
164
+ ORDER BY path`,
165
+ values: [analysisId],
166
+ });
167
+ return result.rows.map((r) => ({ path: r.path, hash: r.hash, fileType: r.file_type ?? null }));
168
+ }
151
169
  /** Count step-output artifacts produced by a run — for the run-completed card. */
152
170
  export async function countArtifactsForRun(pool, analysisId, runId) {
153
171
  const result = await pool.query({
@@ -130,6 +130,37 @@ CREATE TABLE IF NOT EXISTS cortex_plans (
130
130
  CREATE INDEX IF NOT EXISTS idx_cortex_plans_analysis
131
131
  ON cortex_plans(analysis_id, created_at DESC);
132
132
 
133
+ -- Report versions — one immutable row for each recorded report version. A row
134
+ -- holds the block document, the pinned snapshot, and the anchor. A thread holds
135
+ -- at most one version. The named unique constraint on thread_id enforces the
136
+ -- rule, because the report policy binds one version to each report session. The
137
+ -- row outlives its thread. Thus the table has no foreign key to
138
+ -- cortex_analysis_threads, and the row carries the anchor columns (thread_id,
139
+ -- parent_thread_id, parent_seq) directly. A purge of the analysis removes the row
140
+ -- through the analysis_id cascade. The table is append-only, and a correction is
141
+ -- a new version, never an update to a recorded row.
142
+ CREATE TABLE IF NOT EXISTS cortex_report_versions (
143
+ version_id TEXT PRIMARY KEY,
144
+ analysis_id TEXT NOT NULL
145
+ REFERENCES cortex_analysis_state(analysis_id)
146
+ ON DELETE CASCADE,
147
+ thread_id TEXT NOT NULL,
148
+ -- Nullable pair: a root conversation anchor can be absent. A version with no
149
+ -- parent thread carries no parent_seq either.
150
+ parent_thread_id TEXT,
151
+ parent_seq BIGINT,
152
+ parent_version_id TEXT
153
+ REFERENCES cortex_report_versions(version_id)
154
+ ON DELETE SET NULL,
155
+ document JSONB NOT NULL,
156
+ snapshot JSONB NOT NULL,
157
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
158
+ CONSTRAINT cortex_report_versions_one_per_thread UNIQUE (thread_id)
159
+ );
160
+
161
+ CREATE INDEX IF NOT EXISTS idx_cortex_report_versions_analysis
162
+ ON cortex_report_versions(analysis_id);
163
+
133
164
  -- Target assessments — organization-scoped, snapshot-style target dossiers.
134
165
  -- Independent of analyses, projects, runs, and chat threads. The full
135
166
  -- dossier is persisted as JSONB on the row, no separate artifacts table.
@@ -80,6 +80,7 @@ const ANALYSIS_KEYED_DELETES = [
80
80
  { op: "purgeAnalysis.asks", sql: "DELETE FROM cortex_asks WHERE analysis_id = $1" },
81
81
  { op: "purgeAnalysis.askGrants", sql: "DELETE FROM cortex_ask_grants WHERE analysis_id = $1" },
82
82
  { op: "purgeAnalysis.plans", sql: "DELETE FROM cortex_plans WHERE analysis_id = $1" },
83
+ { op: "purgeAnalysis.reportVersions", sql: "DELETE FROM cortex_report_versions WHERE analysis_id = $1" },
83
84
  { op: "purgeAnalysis.analysisState", sql: "DELETE FROM cortex_analysis_state WHERE analysis_id = $1" },
84
85
  ];
85
86
  /**