@inflexa-ai/harness 0.17.0 → 0.19.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 (249) hide show
  1. package/dist/agents/report-builder.d.ts +2 -0
  2. package/dist/agents/report-builder.js +2 -0
  3. package/dist/agents/report-session-agent.d.ts +56 -0
  4. package/dist/agents/report-session-agent.js +72 -0
  5. package/dist/app/report-session-runtime.d.ts +72 -0
  6. package/dist/app/report-session-runtime.js +157 -0
  7. package/dist/app/spawn-report-session.d.ts +85 -0
  8. package/dist/app/spawn-report-session.js +83 -0
  9. package/dist/contracts/chat-events.d.ts +9 -0
  10. package/dist/contracts/index.d.ts +6 -2
  11. package/dist/contracts/index.js +3 -1
  12. package/dist/contracts/organ-system.d.ts +49 -0
  13. package/dist/contracts/organ-system.js +67 -0
  14. package/dist/contracts/report-blocks.d.ts +564 -0
  15. package/dist/contracts/report-blocks.js +117 -0
  16. package/dist/contracts/report-reference.d.ts +459 -0
  17. package/dist/contracts/report-reference.js +256 -0
  18. package/dist/contracts/schemas/chat-events.d.ts +2 -0
  19. package/dist/contracts/schemas/chat-events.js +2 -0
  20. package/dist/contracts/severity.d.ts +15 -0
  21. package/dist/contracts/severity.js +10 -0
  22. package/dist/contracts/target-dossier.d.ts +8675 -12972
  23. package/dist/contracts/target-dossier.js +534 -591
  24. package/dist/data/safety-panel-schema.d.ts +18 -5
  25. package/dist/data/safety-panel-schema.js +4 -4
  26. package/dist/execution/artifact-metadata.js +1 -0
  27. package/dist/execution/report-runner.js +1 -3
  28. package/dist/execution/run-synthesis.js +2 -0
  29. package/dist/index.d.ts +6 -0
  30. package/dist/index.js +19 -0
  31. package/dist/lib/async-utils.d.ts +16 -4
  32. package/dist/lib/async-utils.js +20 -15
  33. package/dist/lib/host-concurrency.js +1 -0
  34. package/dist/loop/__fixtures__/scripted-provider.js +23 -2
  35. package/dist/loop/run-agent.js +58 -10
  36. package/dist/loop/tool-detail.d.ts +40 -0
  37. package/dist/loop/tool-detail.js +35 -3
  38. package/dist/loop/types.d.ts +13 -0
  39. package/dist/memory/thread-history.d.ts +10 -0
  40. package/dist/memory/thread-history.js +12 -1
  41. package/dist/memory/thread-store.d.ts +4 -4
  42. package/dist/memory/thread-store.js +18 -9
  43. package/dist/prompts/report-session.d.ts +1 -0
  44. package/dist/prompts/report-session.js +89 -0
  45. package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +1 -1
  46. package/dist/prompts/target-assessment/briefs/executive-recommendation.js +5 -5
  47. package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +1 -1
  48. package/dist/prompts/target-assessment/briefs/liability-bullets.js +1 -1
  49. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +1 -1
  50. package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +11 -3
  51. package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +1 -0
  52. package/dist/prompts/target-assessment/investigation/adversarial-critique.js +67 -0
  53. package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +1 -0
  54. package/dist/prompts/target-assessment/investigation/claim-reverification.js +49 -0
  55. package/dist/prompts/target-assessment/investigation/index.d.ts +3 -0
  56. package/dist/prompts/target-assessment/investigation/index.js +3 -0
  57. package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +1 -0
  58. package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +49 -0
  59. package/dist/prompts/target-assessment/tox-voice/index.d.ts +1 -1
  60. package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +8 -5
  61. package/dist/prompts/target-assessment/tox-voice/vocabulary.js +9 -20
  62. package/dist/providers/__fixtures__/session.d.ts +5 -4
  63. package/dist/providers/__fixtures__/session.js +4 -4
  64. package/dist/providers/ai-sdk.d.ts +77 -0
  65. package/dist/providers/ai-sdk.js +149 -19
  66. package/dist/providers/errors.d.ts +14 -0
  67. package/dist/providers/errors.js +103 -18
  68. package/dist/providers/types.d.ts +16 -0
  69. package/dist/providers/types.js +10 -1
  70. package/dist/report-model/block-walk.d.ts +56 -0
  71. package/dist/report-model/block-walk.js +89 -0
  72. package/dist/report-model/draft-finish.d.ts +60 -0
  73. package/dist/report-model/draft-finish.js +56 -0
  74. package/dist/report-model/draft-operations.d.ts +125 -0
  75. package/dist/report-model/draft-operations.js +341 -0
  76. package/dist/report-model/draft-read.d.ts +59 -0
  77. package/dist/report-model/draft-read.js +109 -0
  78. package/dist/report-model/draft.d.ts +51 -0
  79. package/dist/report-model/draft.js +32 -0
  80. package/dist/report-model/fixture-resolver.d.ts +8 -0
  81. package/dist/report-model/fixture-resolver.js +252 -0
  82. package/dist/report-model/pin-snapshot.d.ts +30 -0
  83. package/dist/report-model/pin-snapshot.js +40 -0
  84. package/dist/report-model/reference-resolver.d.ts +113 -0
  85. package/dist/report-model/reference-resolver.js +60 -0
  86. package/dist/report-model/structural-validation.d.ts +22 -0
  87. package/dist/report-model/structural-validation.js +68 -0
  88. package/dist/report-model/validate.d.ts +72 -0
  89. package/dist/report-model/validate.js +95 -0
  90. package/dist/report-render/chart.d.ts +33 -0
  91. package/dist/report-render/chart.js +538 -0
  92. package/dist/report-render/page.d.ts +127 -0
  93. package/dist/report-render/page.js +307 -0
  94. package/dist/report-render/references.d.ts +32 -0
  95. package/dist/report-render/references.js +44 -0
  96. package/dist/report-render/render.d.ts +25 -0
  97. package/dist/report-render/render.js +125 -0
  98. package/dist/report-render/script-json.d.ts +13 -0
  99. package/dist/report-render/script-json.js +15 -0
  100. package/dist/report-render/types.d.ts +39 -0
  101. package/dist/report-render/types.js +10 -0
  102. package/dist/report-render/value-bridge.d.ts +62 -0
  103. package/dist/report-render/value-bridge.js +82 -0
  104. package/dist/report-render/views/chart-view.d.ts +11 -0
  105. package/dist/report-render/views/chart-view.js +23 -0
  106. package/dist/report-render/views/page-view.d.ts +20 -0
  107. package/dist/report-render/views/page-view.js +49 -0
  108. package/dist/report-render/views/prose.d.ts +28 -0
  109. package/dist/report-render/views/prose.js +75 -0
  110. package/dist/report-render/views/references-view.d.ts +20 -0
  111. package/dist/report-render/views/references-view.js +64 -0
  112. package/dist/report-render/views/values.d.ts +42 -0
  113. package/dist/report-render/views/values.js +43 -0
  114. package/dist/runtime/assemble.d.ts +15 -0
  115. package/dist/runtime/assemble.js +28 -6
  116. package/dist/runtime/boot.d.ts +3 -3
  117. package/dist/runtime/boot.js +15 -4
  118. package/dist/runtime/dbos.d.ts +4 -4
  119. package/dist/state/artifacts.d.ts +16 -0
  120. package/dist/state/artifacts.js +18 -0
  121. package/dist/state/init.js +54 -0
  122. package/dist/state/purge-analysis.js +2 -0
  123. package/dist/state/report-session-state.d.ts +112 -0
  124. package/dist/state/report-session-state.js +150 -0
  125. package/dist/state/report-versions.d.ts +129 -0
  126. package/dist/state/report-versions.js +167 -0
  127. package/dist/state/snapshot-parse.d.ts +34 -0
  128. package/dist/state/snapshot-parse.js +82 -0
  129. package/dist/tasks/data-profile.js +1 -0
  130. package/dist/tools/ad-hoc-router.d.ts +7 -2
  131. package/dist/tools/ad-hoc-router.js +2 -1
  132. package/dist/tools/bio/chembl.js +1 -0
  133. package/dist/tools/bio/comptox.js +1 -0
  134. package/dist/tools/bio/drug-gene-interactions.js +1 -0
  135. package/dist/tools/bio/gene-disease-evidence.js +1 -0
  136. package/dist/tools/bio/gene-preclinical-profile.js +1 -0
  137. package/dist/tools/bio/keys.d.ts +3 -2
  138. package/dist/tools/bio/keys.js +3 -2
  139. package/dist/tools/bio/lookup-annotation.js +1 -0
  140. package/dist/tools/bio/opentargets.d.ts +3 -3
  141. package/dist/tools/bio/opentargets.js +4 -3
  142. package/dist/tools/bio/pubchem.js +1 -0
  143. package/dist/tools/bio/search-clinical-trials.js +1 -0
  144. package/dist/tools/bio/search-faers.js +1 -0
  145. package/dist/tools/bio/search-geo-datasets.js +1 -0
  146. package/dist/tools/bio/search-interactions.js +1 -0
  147. package/dist/tools/bio/target-safety.d.ts +4 -6
  148. package/dist/tools/bio/target-safety.js +7 -6
  149. package/dist/tools/define-tool.d.ts +19 -12
  150. package/dist/tools/define-tool.js +4 -2
  151. package/dist/tools/display/show-user.js +1 -0
  152. package/dist/tools/iterate-report.js +2 -0
  153. package/dist/tools/lib/chembl-client.d.ts +2 -0
  154. package/dist/tools/lib/chembl-client.js +1 -0
  155. package/dist/tools/lib/monarch-client.d.ts +42 -0
  156. package/dist/tools/lib/monarch-client.js +100 -0
  157. package/dist/tools/report/build-report.js +1 -0
  158. package/dist/tools/report/mint-preview-url.js +1 -0
  159. package/dist/tools/report/preview-snapshot.js +1 -0
  160. package/dist/tools/report/submit-report.js +1 -0
  161. package/dist/tools/report/version-fs.js +4 -0
  162. package/dist/tools/report-authoring/authoring-tools.d.ts +241 -0
  163. package/dist/tools/report-authoring/authoring-tools.js +460 -0
  164. package/dist/tools/report-session/index.d.ts +8 -0
  165. package/dist/tools/report-session/index.js +8 -0
  166. package/dist/tools/report-session/preview-report.d.ts +90 -0
  167. package/dist/tools/report-session/preview-report.js +245 -0
  168. package/dist/tools/research/context7-docs.js +2 -0
  169. package/dist/tools/research/generate-analogy-report.js +1 -0
  170. package/dist/tools/research/generate-plan.js +7 -2
  171. package/dist/tools/research/inspect-data-profile.js +4 -0
  172. package/dist/tools/research/literature-reviewer.js +1 -0
  173. package/dist/tools/research/search-arxiv.js +1 -0
  174. package/dist/tools/research/search-github-repos.js +1 -0
  175. package/dist/tools/research/search-semantic-scholar.js +1 -0
  176. package/dist/tools/sandbox/list-available-packages.js +23 -0
  177. package/dist/tools/sandbox/list-available-refs.js +36 -0
  178. package/dist/tools/sandbox/report-blocker.js +1 -0
  179. package/dist/tools/sandbox/skills.js +2 -0
  180. package/dist/tools/workspace/file-stat.js +1 -0
  181. package/dist/tools/workspace/show-file.js +2 -0
  182. package/dist/tools/workspace/show-plan.js +1 -0
  183. package/dist/workflows/execute-analysis.d.ts +5 -1
  184. package/dist/workflows/execute-analysis.js +5 -5
  185. package/dist/workflows/execute-target-assessment.d.ts +13 -2
  186. package/dist/workflows/execute-target-assessment.js +78 -16
  187. package/dist/workflows/target-assessment/assemblers/index.d.ts +1 -1
  188. package/dist/workflows/target-assessment/assemblers/index.js +1 -1
  189. package/dist/workflows/target-assessment/assemblers/literature.d.ts +9 -27
  190. package/dist/workflows/target-assessment/assemblers/literature.js +32 -48
  191. package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +17 -9
  192. package/dist/workflows/target-assessment/assemblers/orchestrator.js +195 -85
  193. package/dist/workflows/target-assessment/assemblers/safety.d.ts +70 -48
  194. package/dist/workflows/target-assessment/assemblers/safety.js +125 -42
  195. package/dist/workflows/target-assessment/collectors/index.d.ts +5 -2
  196. package/dist/workflows/target-assessment/collectors/index.js +48 -5
  197. package/dist/workflows/target-assessment/coverage.d.ts +43 -27
  198. package/dist/workflows/target-assessment/coverage.js +33 -29
  199. package/dist/workflows/target-assessment/fanout/index.js +1 -0
  200. package/dist/workflows/target-assessment/investigation/index.d.ts +97 -0
  201. package/dist/workflows/target-assessment/investigation/index.js +419 -0
  202. package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +26 -5
  203. package/dist/workflows/target-assessment/lib/approval-precedents.js +95 -29
  204. package/dist/workflows/target-assessment/lib/citation-validator.js +1 -1
  205. package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +2 -2
  206. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +2 -3
  207. package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -4
  208. package/dist/workflows/target-assessment/lib/compute-derived.d.ts +6 -5
  209. package/dist/workflows/target-assessment/lib/compute-derived.js +14 -5
  210. package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +5 -6
  211. package/dist/workflows/target-assessment/lib/family-complex-supplement.js +2 -3
  212. package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +72 -0
  213. package/dist/workflows/target-assessment/lib/fda-label-safety.js +166 -0
  214. package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -9
  215. package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -10
  216. package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +34 -0
  217. package/dist/workflows/target-assessment/lib/hpo-organ-map.js +63 -0
  218. package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +19 -0
  219. package/dist/workflows/target-assessment/lib/impc-organ-map.js +39 -0
  220. package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +24 -14
  221. package/dist/workflows/target-assessment/lib/meddra-organ-map.js +59 -47
  222. package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +1 -1
  223. package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +2 -3
  224. package/dist/workflows/target-assessment/lib/regulatory-actions.js +33 -4
  225. package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +44 -0
  226. package/dist/workflows/target-assessment/lib/safety-corroboration.js +273 -0
  227. package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +5 -7
  228. package/dist/workflows/target-assessment/lib/target-identity-filter.js +5 -7
  229. package/dist/workflows/target-assessment/metrics.d.ts +1 -6
  230. package/dist/workflows/target-assessment/metrics.js +1 -13
  231. package/dist/workflows/target-assessment/phase4-assemble.d.ts +2147 -807
  232. package/dist/workflows/target-assessment/phase4-assemble.js +19 -19
  233. package/dist/workflows/target-assessment/phase5-persist.d.ts +20 -21
  234. package/dist/workflows/target-assessment/phase5-persist.js +56 -173
  235. package/dist/workflows/target-assessment/schemas.d.ts +46 -1
  236. package/dist/workflows/target-assessment/schemas.js +19 -0
  237. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +1 -0
  238. package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +2 -0
  239. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +26 -62
  240. package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -4
  241. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +27 -154
  242. package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -6
  243. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -6
  244. package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -18
  245. package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +1 -0
  246. package/dist/workflows/target-assessment/synthesis/index.d.ts +52 -5
  247. package/dist/workflows/target-assessment/synthesis/index.js +4 -1
  248. package/dist/workflows/target-assessment/synthesis-probes.d.ts +3 -3
  249. package/package.json +4 -1
@@ -3,11 +3,9 @@
3
3
  * sources that carry it: the curated secondary-pharmacology panel that ships
4
4
  * with the harness, and Open Targets' curated liabilities.
5
5
  *
6
- * These were `check_safety_panel` and `opentargets({action:'safety'})`, and the
7
- * panel tool's own description instructed calling them together — a pairing
8
- * every caller had to remember, expressed in prose that had already gone stale
9
- * (it named two tools that no longer exist). Both are keyed on the same target
10
- * and answer the same question, so the pairing belongs in the tool.
6
+ * Both sources are keyed on the same target and answer the same question, so
7
+ * the pairing belongs in the tool rather than in prose a caller has to
8
+ * remember: one call consults both and merges what they carry.
11
9
  *
12
10
  * Identifier resolution is the tool's job, not the caller's: Open Targets
13
11
  * accepts ONLY an Ensembl gene id, and it is taken from the panel entry when
@@ -41,7 +39,7 @@ export declare const targetSafetyTool: import("../define-tool.js").Tool<{
41
39
  identifiers: string[];
42
40
  identifierType?: "auto" | "gene_symbol" | "uniprot" | "chembl_id" | "ensembl_id" | undefined;
43
41
  sources?: ("opentargets" | "panel")[] | undefined;
44
- filterOrgan?: "hematologic" | "metabolic" | "immune" | "hepatic" | "cns" | "renal" | "respiratory" | "cardiac" | "gi" | undefined;
42
+ filterOrgan?: "hematologic" | "metabolic" | "immune" | "hepatic" | "musculoskeletal" | "cns" | "renal" | "reproductive" | "respiratory" | "cardiac" | "vascular" | "pns" | "gi" | "pancreas" | "endocrine_thyroid" | "dermatologic" | "ocular" | "oncology" | undefined;
45
43
  minSeverity?: "high" | "medium" | "low" | undefined;
46
44
  liabilityLimit?: number | undefined;
47
45
  }, {
@@ -3,11 +3,9 @@
3
3
  * sources that carry it: the curated secondary-pharmacology panel that ships
4
4
  * with the harness, and Open Targets' curated liabilities.
5
5
  *
6
- * These were `check_safety_panel` and `opentargets({action:'safety'})`, and the
7
- * panel tool's own description instructed calling them together — a pairing
8
- * every caller had to remember, expressed in prose that had already gone stale
9
- * (it named two tools that no longer exist). Both are keyed on the same target
10
- * and answer the same question, so the pairing belongs in the tool.
6
+ * Both sources are keyed on the same target and answer the same question, so
7
+ * the pairing belongs in the tool rather than in prose a caller has to
8
+ * remember: one call consults both and merges what they carry.
11
9
  *
12
10
  * Identifier resolution is the tool's job, not the caller's: Open Targets
13
11
  * accepts ONLY an Ensembl gene id, and it is taken from the panel entry when
@@ -15,7 +13,9 @@
15
13
  */
16
14
  import { ok } from "neverthrow";
17
15
  import { z } from "zod";
18
- import { CHEMBL_RE, ORGAN_SYSTEMS, SEVERITIES, SafetyPanelFileSchema, UNIPROT_RE, } from "../../data/safety-panel-schema.js";
16
+ import { ORGAN_SYSTEMS } from "../../contracts/organ-system.js";
17
+ import { SEVERITIES } from "../../contracts/severity.js";
18
+ import { CHEMBL_RE, SafetyPanelFileSchema, UNIPROT_RE } from "../../data/safety-panel-schema.js";
19
19
  import panelData from "../../data/safety-panel.json" with { type: "json" };
20
20
  import { defineTool } from "../define-tool.js";
21
21
  import { resolveSymbolToEnsemblId } from "../lib/ensembl-client.js";
@@ -100,6 +100,7 @@ export const targetSafetyTool = defineTool({
100
100
  "means no CURATED liability, not that none exists. Both are valid no-data — do not retry. Read `perSource` and each row's `opentargetsNote`: a " +
101
101
  "ChEMBL or UniProt identifier absent from the panel cannot be resolved to an Ensembl id, so its Open Targets half is skipped rather than empty.",
102
102
  inputSchema,
103
+ describeCall: "none",
103
104
  execute: async ({ identifiers, identifierType, sources, filterOrgan, minSeverity, liabilityLimit }) => {
104
105
  const selected = sources ?? [...SOURCES];
105
106
  const wantPanel = selected.includes("panel");
@@ -95,20 +95,28 @@ export interface ToolDefinition<Schema extends z.ZodType, Output> {
95
95
  readonly inputSchema: Schema;
96
96
  readonly executionMode?: ToolExecutionMode;
97
97
  /**
98
- * One line naming what THIS call is doing — the call-time counterpart of
98
+ * One line that names what THIS call does — the call-time counterpart of
99
99
  * `description`. `description` self-describes the tool at attach time, so an
100
- * agent knows what it holds; `describeCall` self-describes one invocation, so
100
+ * agent knows what it holds. `describeCall` self-describes one invocation, so
101
101
  * a surface can render four `update_working_memory` calls as four distinct
102
102
  * lines instead of four identical chips.
103
103
  *
104
- * It lives here, beside `inputSchema`, because that is the one place the
105
- * compiler checks the two against each other. A host-side formatter reading
106
- * `input.path` by string key breaks silently the day the schema moves; this
107
- * one fails to build. The tool author writes it because the tool author knows
108
- * which field matters.
104
+ * The decision is REQUIRED. Give a hook, or give the literal `"none"`.
105
+ * `"none"` declares that the input of the tool cannot distinguish its calls,
106
+ * thus a hook can only restate the name of the tool. `defineTool` consumes
107
+ * the sentinel at construction, and the sentinel never reaches a packaged
108
+ * tool. Thus a reader of a `Tool` sees a function, or sees nothing. A hook
109
+ * that returns the string `"none"` is still a hook, because the discriminator
110
+ * is `typeof`, not equality.
109
111
  *
110
- * Synchronous and pure — no I/O, no ambient state. Dispatch is what the user
111
- * waits on, and a description must never be able to fail a call: the loop
112
+ * A hook lives here, beside `inputSchema`, because that is the one place
113
+ * where the compiler checks the two against each other. A host-side formatter
114
+ * that reads `input.path` by string key breaks silently the day the schema
115
+ * moves. This one fails to build. The tool author writes it, because the tool
116
+ * author knows which field matters.
117
+ *
118
+ * A hook is synchronous and pure — no I/O, no ambient state. Dispatch is what
119
+ * the user waits on, and a description must never fail a call: the loop
112
120
  * validates the input against `inputSchema` first, guards the call, and drops
113
121
  * the detail on any failure (see the tool-call-detail capability).
114
122
  *
@@ -116,10 +124,9 @@ export interface ToolDefinition<Schema extends z.ZodType, Output> {
116
124
  * removed, secrets redacted, length capped — happens once at the emit site,
117
125
  * never here.
118
126
  *
119
- * OPTIONAL, and absence is a normal state: a tool without it is observable
120
- * exactly as it is today. The hook never reaches the model.
127
+ * The hook never reaches the model.
121
128
  */
122
- describeCall?(input: z.infer<Schema>): string;
129
+ describeCall: ((input: z.infer<Schema>) => string) | "none";
123
130
  execute(input: z.infer<Schema>, ctx: ToolContext): Promise<Result<Output, ToolError>>;
124
131
  }
125
132
  /**
@@ -44,9 +44,11 @@ export function defineTool(def) {
44
44
  inputSchema: def.inputSchema,
45
45
  jsonSchema,
46
46
  executionMode,
47
- // Spread rather than assigned: a tool that declares no hook carries no
47
+ // Spread rather than assigned: a tool that declines carries no
48
48
  // `describeCall` key at all, so "has a hook" is one property check.
49
- ...(def.describeCall === undefined ? {} : { describeCall: def.describeCall }),
49
+ // `typeof` is the discriminator, not equality with the sentinel, so a
50
+ // hook that returns the string `"none"` still packages as a hook.
51
+ ...(typeof def.describeCall === "function" ? { describeCall: def.describeCall } : {}),
50
52
  execute: def.execute,
51
53
  };
52
54
  }
@@ -61,6 +61,7 @@ export const showUserTool = defineTool({
61
61
  "One card per call, rendered in call order; an identical repeat call resolves to the same card, so compose the " +
62
62
  "finished content before calling rather than re-rendering a draft.",
63
63
  inputSchema: ShowUserInputSchema,
64
+ describeCall: ({ kind, title }) => (title === undefined ? kind : `${kind}: ${title}`),
64
65
  execute: async (input, ctx) => {
65
66
  // `dataPath` is a reference the host resolves at render time; validate its
66
67
  // shape only (existence is a render-time host concern), and treat a
@@ -413,6 +413,7 @@ export const planReportTool = defineTool({
413
413
  "existing preview with `modifications` needs no brief — you may go " +
414
414
  "straight to `submit_report`.)",
415
415
  inputSchema: z.object({}),
416
+ describeCall: "none",
416
417
  executionMode: "inline",
417
418
  execute: async () => ok({
418
419
  schema: REPORT_BRIEF_JSON_SCHEMA,
@@ -472,6 +473,7 @@ export function createReportSubmitTool(deps) {
472
473
  "as an `error` string. Returns the preview id + version and emits a " +
473
474
  "preview card.",
474
475
  inputSchema: submitReportInputSchema,
476
+ describeCall: "none",
475
477
  execute: async (input, ctx) => {
476
478
  // The brief rides as `unknown` so its ~12k schema stays off the
477
479
  // always-on tool surface. Validate it here and return issues as DATA
@@ -58,6 +58,8 @@ export interface ChemblPolypharmHit {
58
58
  moleculeChemblId: string;
59
59
  targetChemblId: string;
60
60
  targetName: string | null;
61
+ /** The assay the reported activity was measured in, when ChEMBL names one. */
62
+ assayChemblId: string | null;
61
63
  standardType: string | null;
62
64
  standardValue: number | null;
63
65
  standardUnits: string | null;
@@ -431,6 +431,7 @@ export async function getModulatorPolypharmacology(moleculeChemblId, options = {
431
431
  moleculeChemblId,
432
432
  targetChemblId: tid,
433
433
  targetName: null,
434
+ assayChemblId: raw.assay_chembl_id ?? null,
434
435
  standardType: raw.standard_type ?? null,
435
436
  standardValue: parseNumeric(raw.standard_value),
436
437
  standardUnits: raw.standard_units ?? null,
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Pure async client functions for the Monarch Initiative association API.
3
+ *
4
+ * Monarch curates human gene→phenotype annotations (HPO, from HPOA/OMIM/
5
+ * Orphanet): *human* loss-of-function evidence, meaning what is observed in
6
+ * people carrying variation in the gene, as opposed to a mouse knockout's
7
+ * phenotype or an aggregate gene-disease association score.
8
+ *
9
+ * Each phenotype association carries its HPO ancestor closure, which is what
10
+ * lets a consumer resolve the phenotype onto an organ system by identifier
11
+ * rather than by matching prose.
12
+ *
13
+ * The API is keyless and public.
14
+ */
15
+ /** A curated human phenotype attributed to variation in the gene. */
16
+ export interface MonarchPhenotypeAssociation {
17
+ /** HPO term id, e.g. `HP:0001392`. */
18
+ readonly hpoId: string;
19
+ readonly label: string;
20
+ /** The term's HPO ancestor closure, for identifier-based organ resolution. */
21
+ readonly ancestorIds: string[];
22
+ /** PMIDs (as `PMID:…` curies) cited by the annotation. */
23
+ readonly publications: string[];
24
+ /** The disease the phenotype was annotated under, when the annotation names one. */
25
+ readonly diseaseContext: string | null;
26
+ /** Share of affected individuals showing the phenotype, when annotated. */
27
+ readonly frequencyPercent: number | null;
28
+ readonly primaryKnowledgeSource: string | null;
29
+ }
30
+ export interface MonarchGeneProfile {
31
+ /** The curie the gene was queried by, e.g. `HGNC:1097`. */
32
+ readonly geneCurie: string;
33
+ readonly phenotypes: MonarchPhenotypeAssociation[];
34
+ }
35
+ /**
36
+ * Curated human phenotype associations for one gene.
37
+ *
38
+ * `geneCurie` is a prefixed identifier Monarch resolves — an HGNC id
39
+ * (`HGNC:1097`) or an NCBI gene id (`NCBIGene:673`). A gene Monarch does not
40
+ * hold returns an empty profile rather than an error.
41
+ */
42
+ export declare function getGenePhenotypeProfile(geneCurie: string): Promise<MonarchGeneProfile>;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Pure async client functions for the Monarch Initiative association API.
3
+ *
4
+ * Monarch curates human gene→phenotype annotations (HPO, from HPOA/OMIM/
5
+ * Orphanet): *human* loss-of-function evidence, meaning what is observed in
6
+ * people carrying variation in the gene, as opposed to a mouse knockout's
7
+ * phenotype or an aggregate gene-disease association score.
8
+ *
9
+ * Each phenotype association carries its HPO ancestor closure, which is what
10
+ * lets a consumer resolve the phenotype onto an organ system by identifier
11
+ * rather than by matching prose.
12
+ *
13
+ * The API is keyless and public.
14
+ */
15
+ import { z } from "zod";
16
+ import { apiFetchValidated, describeApiError, isUnexpectedApiError } from "./api-utils.js";
17
+ const MONARCH_BASE = "https://api-v3.monarchinitiative.org/v3/api";
18
+ /** Row cap per association category — a well-annotated gene carries a few hundred. */
19
+ const ASSOCIATION_ROW_CAP = 300;
20
+ /** The closure arrays make these responses large; the default timeout is short for them. */
21
+ const REQUEST_TIMEOUT_MS = 120_000;
22
+ const HUMAN_TAXON = "NCBITaxon:9606";
23
+ /** Phenotype terms live in the HPO namespace; a model-organism term would not. */
24
+ const HPO_NAMESPACE = "HP";
25
+ /**
26
+ * One association row. Every field is optional because Monarch returns the same
27
+ * envelope for every category and populates only what applies; the reader below
28
+ * treats a missing field as absent rather than trusting the wire.
29
+ */
30
+ const MonarchAssociationSchema = z.object({
31
+ subject_taxon: z.string().nullish(),
32
+ object: z.string().optional(),
33
+ object_label: z.string().nullish(),
34
+ object_closure: z.array(z.string()).nullish(),
35
+ publications: z.array(z.string()).nullish(),
36
+ primary_knowledge_source: z.string().nullish(),
37
+ disease_context_qualifier: z.string().nullish(),
38
+ has_percentage: z.number().nullish(),
39
+ });
40
+ const MonarchAssociationPageSchema = z.object({
41
+ items: z.array(MonarchAssociationSchema).optional(),
42
+ });
43
+ async function fetchAssociations(geneCurie, category) {
44
+ const params = new URLSearchParams({
45
+ subject: geneCurie,
46
+ category,
47
+ limit: String(ASSOCIATION_ROW_CAP),
48
+ });
49
+ const res = await apiFetchValidated(`${MONARCH_BASE}/association?${params.toString()}`, MonarchAssociationPageSchema, {
50
+ timeoutMs: REQUEST_TIMEOUT_MS,
51
+ });
52
+ if (res.isErr()) {
53
+ // A 4xx means the gene curie is not one Monarch holds — an expected
54
+ // "not found", returned as an empty page. Anything else is a real
55
+ // failure and surfaces.
56
+ if (isUnexpectedApiError(res.error))
57
+ throw new Error(describeApiError(res.error));
58
+ return [];
59
+ }
60
+ return res.value.items ?? [];
61
+ }
62
+ /**
63
+ * Human phenotype annotations only.
64
+ *
65
+ * Monarch serves a gene's phenotype edges from several annotation sets; the
66
+ * filter keeps the rows whose subject is the human gene and whose object is an
67
+ * HPO term, so model-organism phenotypes stay with the collector that owns them.
68
+ */
69
+ function toPhenotype(row) {
70
+ if (!row.object || !row.object.startsWith(`${HPO_NAMESPACE}:`))
71
+ return null;
72
+ if (row.subject_taxon != null && row.subject_taxon !== HUMAN_TAXON)
73
+ return null;
74
+ return {
75
+ hpoId: row.object,
76
+ label: row.object_label ?? "",
77
+ ancestorIds: row.object_closure ?? [],
78
+ publications: row.publications ?? [],
79
+ diseaseContext: row.disease_context_qualifier ?? null,
80
+ frequencyPercent: row.has_percentage ?? null,
81
+ primaryKnowledgeSource: row.primary_knowledge_source ?? null,
82
+ };
83
+ }
84
+ /**
85
+ * Curated human phenotype associations for one gene.
86
+ *
87
+ * `geneCurie` is a prefixed identifier Monarch resolves — an HGNC id
88
+ * (`HGNC:1097`) or an NCBI gene id (`NCBIGene:673`). A gene Monarch does not
89
+ * hold returns an empty profile rather than an error.
90
+ */
91
+ export async function getGenePhenotypeProfile(geneCurie) {
92
+ const rows = await fetchAssociations(geneCurie, "biolink:GeneToPhenotypicFeatureAssociation");
93
+ const phenotypes = [];
94
+ for (const row of rows) {
95
+ const p = toPhenotype(row);
96
+ if (p)
97
+ phenotypes.push(p);
98
+ }
99
+ return { geneCurie, phenotypes };
100
+ }
@@ -16,6 +16,7 @@ export function createBuildReportTool(state) {
16
16
  "Always use this — never invoke a build script directly. Returns " +
17
17
  "structured errors with line numbers when the template is broken.",
18
18
  inputSchema: z.object({}),
19
+ describeCall: "none",
19
20
  execute: async () => {
20
21
  return ok(await renderReport({
21
22
  versionDir: state.versionDir,
@@ -24,6 +24,7 @@ export function createMintPreviewUrlTool(state) {
24
24
  inputSchema: z.object({
25
25
  version: z.number().optional().describe("Version to mint for. Defaults to the iteration's current version."),
26
26
  }),
27
+ describeCall: "none",
27
28
  execute: async (input) => {
28
29
  const version = input.version ?? state.currentVersion;
29
30
  const result = await state.previews.mintPreviewAccess(state.resourceId, state.previewId);
@@ -47,6 +47,7 @@ export function createPreviewSnapshotTool(state) {
47
47
  .optional()
48
48
  .describe("Optional extra wait in ms after the theme-ready event (e.g., for chart paint). Capped at 30s."),
49
49
  }),
50
+ describeCall: "none",
50
51
  execute: async (input) => {
51
52
  let url = state.urlCell.url;
52
53
  const expiresAt = state.urlCell.expiresAt;
@@ -42,6 +42,7 @@ export function createSubmitReportTool(state) {
42
42
  "samples — the rest had no QC row'. " +
43
43
  "Leave empty when the report shipped cleanly."),
44
44
  }),
45
+ describeCall: "none",
45
46
  execute: async (input) => {
46
47
  const problems = [];
47
48
  // 1. report.html.j2 exists — the report itself, as opposed to the
@@ -44,6 +44,7 @@ export function createVersionFsTools(state) {
44
44
  path: z.string().min(1),
45
45
  content: z.string(),
46
46
  }),
47
+ describeCall: "none",
47
48
  execute: async ({ path, content }) => {
48
49
  const resolved = resolveAgentPath(path, state.versionDir);
49
50
  if (resolved.kind === "out_of_scope") {
@@ -73,6 +74,7 @@ export function createVersionFsTools(state) {
73
74
  oldText: z.string().min(1),
74
75
  newText: z.string(),
75
76
  }),
77
+ describeCall: "none",
76
78
  execute: async ({ path, oldText, newText }) => {
77
79
  const resolved = resolveAgentPath(path, state.versionDir);
78
80
  if (resolved.kind === "out_of_scope") {
@@ -112,6 +114,7 @@ export function createVersionFsTools(state) {
112
114
  inputSchema: z.object({
113
115
  path: z.string().min(1),
114
116
  }),
117
+ describeCall: "none",
115
118
  execute: async ({ path }) => {
116
119
  const resolved = resolveAgentPath(path, state.versionDir);
117
120
  if (resolved.kind === "out_of_scope") {
@@ -148,6 +151,7 @@ export function createVersionFsTools(state) {
148
151
  inputSchema: z.object({
149
152
  path: z.string().min(1),
150
153
  }),
154
+ describeCall: "none",
151
155
  execute: async ({ path }) => {
152
156
  const resolved = resolveAgentPath(path, state.versionDir);
153
157
  if (resolved.kind === "out_of_scope") {
@@ -0,0 +1,241 @@
1
+ /**
2
+ * The authoring tool surface over the draft operations.
3
+ *
4
+ * The factory takes a session-state gateway, and it holds no draft. Each tool reads the thread id from the
5
+ * scope of the call, loads the state of that thread, applies the pure operation, and persists the new
6
+ * document. Thus two threads never share one draft, and a landed document outlives a host restart.
7
+ *
8
+ * Each tool maps its flat input onto a core operation, and it applies the pure operation. A landed
9
+ * document persists before the tool reports `applied: true`. A refused operation returns typed data in the
10
+ * ok channel, and the error channel stays for an unexpected failure.
11
+ *
12
+ * A call with no report thread in its scope, a thread with no stored state, and a gateway fault each
13
+ * refuse as typed data too. That refusal sits beside the core refusal, and it names the absent thread
14
+ * scope or the absent state.
15
+ *
16
+ * The primitive admits a flat object input only. Thus a destination is four optional fields, and not a
17
+ * nested union. A conflict between the flat fields refuses before the core runs.
18
+ */
19
+ import { type Result } from "neverthrow";
20
+ import { z } from "zod";
21
+ import type { Scope } from "../../auth/types.js";
22
+ import { type DraftRefusal } from "../../report-model/draft-operations.js";
23
+ import { type OutlineEntry, type ReadableBlock } from "../../report-model/draft-read.js";
24
+ import { type FinishResult } from "../../report-model/draft-finish.js";
25
+ import { type DraftDocument } from "../../report-model/draft.js";
26
+ import type { ReportSnapshot } from "../../report-model/reference-resolver.js";
27
+ import { type Tool } from "../define-tool.js";
28
+ /** The flat destination fields. An anchor is `before` or `after`, and a place is `start` or `end`. */
29
+ declare const addBlockInput: z.ZodObject<{
30
+ block: z.ZodUnion<readonly [z.ZodType<import("../../report-model/draft.js").DraftBlock, unknown, z.core.$ZodTypeInternals<import("../../report-model/draft.js").DraftBlock, unknown>>, z.ZodUnknown]>;
31
+ parentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
32
+ place: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
33
+ end: "end";
34
+ start: "start";
35
+ }>>>;
36
+ before: z.ZodOptional<z.ZodNullable<z.ZodString>>;
37
+ after: z.ZodOptional<z.ZodNullable<z.ZodString>>;
38
+ }, z.core.$strip>;
39
+ /**
40
+ * The change payload is a title for a section target, or a block for an atom target, and exactly one of
41
+ * the two. Both fields are absentable, because a section retitle names no block. Zod 4 treats a bare
42
+ * `z.unknown()` as a required key, thus an optional marker is what makes the retitle call representable.
43
+ */
44
+ declare const changeBlockInput: z.ZodObject<{
45
+ targetId: z.ZodString;
46
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
47
+ block: z.ZodOptional<z.ZodUnion<readonly [z.ZodType<import("../../report-model/draft.js").DraftBlock, unknown, z.core.$ZodTypeInternals<import("../../report-model/draft.js").DraftBlock, unknown>>, z.ZodUnknown]>>;
48
+ }, z.core.$strip>;
49
+ declare const removeBlockInput: z.ZodObject<{
50
+ targetId: z.ZodString;
51
+ }, z.core.$strip>;
52
+ declare const moveBlockInput: z.ZodObject<{
53
+ targetId: z.ZodString;
54
+ parentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
55
+ place: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
56
+ end: "end";
57
+ start: "start";
58
+ }>>>;
59
+ before: z.ZodOptional<z.ZodNullable<z.ZodString>>;
60
+ after: z.ZodOptional<z.ZodNullable<z.ZodString>>;
61
+ }, z.core.$strip>;
62
+ declare const readOutlineInput: z.ZodObject<{}, z.core.$strip>;
63
+ /** The read names its target with the same field name as each mutation, thus one id key serves them all. */
64
+ declare const readBlockInput: z.ZodObject<{
65
+ targetId: z.ZodString;
66
+ }, z.core.$strip>;
67
+ declare const setTitleInput: z.ZodObject<{
68
+ title: z.ZodString;
69
+ }, z.core.$strip>;
70
+ declare const finishDraftInput: z.ZodObject<{}, z.core.$strip>;
71
+ export type AddBlockInput = z.infer<typeof addBlockInput>;
72
+ export type ChangeBlockInput = z.infer<typeof changeBlockInput>;
73
+ export type RemoveBlockInput = z.infer<typeof removeBlockInput>;
74
+ export type MoveBlockInput = z.infer<typeof moveBlockInput>;
75
+ export type ReadOutlineInput = z.infer<typeof readOutlineInput>;
76
+ export type ReadBlockInput = z.infer<typeof readBlockInput>;
77
+ export type SetTitleInput = z.infer<typeof setTitleInput>;
78
+ export type FinishDraftInput = z.infer<typeof finishDraftInput>;
79
+ /** One container that an operation changed, and the child order inside it after the operation. */
80
+ export interface ChangedContainer {
81
+ /** The section that holds the children. It is absent when the container is the root. */
82
+ parentId?: string;
83
+ children: OutlineEntry[];
84
+ }
85
+ /**
86
+ * The closed set of tool-layer refusal reasons.
87
+ *
88
+ * `no-thread-scope` means that the scope of the call names no report thread. `absent-state` means that no
89
+ * report session exists for the thread, and it is permanent. `wrong-thread-type` means that the thread is
90
+ * not a report thread, and it is permanent. `scope-analysis-mismatch` means that the scope names one
91
+ * analysis and the thread belongs to another, and it is permanent. `stale-state` means that a concurrent
92
+ * turn landed first, and the agent must read the state again. `state-unavailable` means that the gateway
93
+ * cannot serve or store the state, and it is transient. The set is disjoint from the core
94
+ * `DraftRefusalReason`, thus a reader tells a core refusal from a tool-layer refusal by the reason alone.
95
+ */
96
+ export type SessionRefusalReason = "no-thread-scope" | "absent-state" | "wrong-thread-type" | "scope-analysis-mismatch" | "stale-state" | "state-unavailable";
97
+ /**
98
+ * A refusal that the tool layer raises before the core runs.
99
+ *
100
+ * The core refuses a bad edit. This layer refuses a call that names no thread, a thread with no stored
101
+ * state, or a gateway that cannot give the state. It sits beside the core `DraftRefusal`, and it never
102
+ * widens the closed reason set of the core.
103
+ */
104
+ export interface SessionRefusal {
105
+ reason: SessionRefusalReason;
106
+ detail: string;
107
+ }
108
+ /**
109
+ * The result of a mutation tool. A refusal carries the typed reason, from the core or from the tool layer.
110
+ *
111
+ * A landing carries the child order of each container that it changed, and not the whole outline. A whole
112
+ * outline costs the size of the draft on every landing, thus composing a report of n blocks would spend
113
+ * n-squared outline entries of agent context to author it. Only the container that the operation touched
114
+ * can surprise the agent: it chose the id, and the rest of the tree did not move. `read_outline` stays
115
+ * the way to read the whole draft, and it costs one call when the agent wants one.
116
+ *
117
+ * A move across two containers reports both. An operation that changes no child order reports none.
118
+ */
119
+ export type MutationResult = {
120
+ applied: true;
121
+ changed: ChangedContainer[];
122
+ } | {
123
+ applied: false;
124
+ refusal: DraftRefusal | SessionRefusal;
125
+ };
126
+ /** The result of `read_outline`. A load that gives no state refuses instead of an outline. */
127
+ export type OutlineResult = {
128
+ outline: OutlineEntry[];
129
+ } | {
130
+ refused: SessionRefusal;
131
+ };
132
+ /**
133
+ * The result of `read_block`. An absent block is an expected outcome, thus it stays in the ok channel. A
134
+ * load that gives no state refuses instead of a block.
135
+ */
136
+ export type ReadBlockResult = {
137
+ found: true;
138
+ block: ReadableBlock;
139
+ } | {
140
+ found: false;
141
+ } | {
142
+ refused: SessionRefusal;
143
+ };
144
+ /** The result of `finish_draft`. A load that gives no state refuses instead of a finish outcome. */
145
+ export type FinishToolResult = FinishResult | {
146
+ refused: SessionRefusal;
147
+ };
148
+ /** The state of one report thread: the document under composition, and the frozen snapshot. */
149
+ export interface ReportSessionState {
150
+ readonly document: DraftDocument;
151
+ readonly snapshot: ReportSnapshot;
152
+ }
153
+ /**
154
+ * The concurrency token that a load hands out and a persist compares against. It is the prior document
155
+ * that the load read, or `null` before the first document lands. The persist lands only when the row still
156
+ * holds it, thus a concurrent turn that landed first turns the next persist into a conflict.
157
+ */
158
+ export type SessionStateToken = DraftDocument | null;
159
+ /**
160
+ * The outcome of a gateway load. `found` carries the state, the analysis that owns the thread, and the
161
+ * concurrency token. `absent` and `wrong-type` are permanent conditions, and `failed` is a transient fault
162
+ * that names its cause.
163
+ */
164
+ export type SessionStateLoad = {
165
+ outcome: "found";
166
+ state: ReportSessionState;
167
+ analysisId: string;
168
+ token: SessionStateToken;
169
+ } | {
170
+ outcome: "absent";
171
+ } | {
172
+ outcome: "wrong-type";
173
+ detail: string;
174
+ } | {
175
+ outcome: "failed";
176
+ detail: string;
177
+ };
178
+ /** The outcome of a gateway persist. `conflict` means that a concurrent turn landed first. */
179
+ export type SessionStatePersist = {
180
+ outcome: "persisted";
181
+ } | {
182
+ outcome: "conflict";
183
+ } | {
184
+ outcome: "failed";
185
+ detail: string;
186
+ };
187
+ /**
188
+ * The session-state gateway that the tools read and write.
189
+ *
190
+ * The tool layer is the one consumer, thus the interface lives here. The interface speaks a plain
191
+ * discriminated value, not a `Result`, because the tools read the outcome as data. The runtime realizes
192
+ * the gateway over the durable store. A load gives the state, a typed absence, or a typed failure. A
193
+ * persist gives success, or a typed failure.
194
+ */
195
+ export interface ReportSessionStateGateway {
196
+ load(threadId: string): Promise<SessionStateLoad>;
197
+ persist(threadId: string, document: DraftDocument, expected: SessionStateToken): Promise<SessionStatePersist>;
198
+ }
199
+ /** The thread of a call, its analysis, the loaded state, and the concurrency token that the persist compares against. */
200
+ export interface OpenedThread {
201
+ readonly threadId: string;
202
+ readonly analysisId: string;
203
+ readonly state: ReportSessionState;
204
+ readonly token: SessionStateToken;
205
+ }
206
+ /**
207
+ * Resolve the report thread of a call, and load its state through the gateway.
208
+ *
209
+ * The report thread and its analysis ride on the analysis scope. The thread id becomes one segment of a
210
+ * session directory, thus the safe-id check sits here beside the shape check: a scope whose id carries a
211
+ * separator or a traversal segment names no thread that a tool can write under.
212
+ *
213
+ * The stored analysis of the thread must match the scope. A mismatch reads one analysis's draft and writes
214
+ * into another analysis's workspace, thus the resolution refuses it with a permanent reason that names the
215
+ * two ids. A scope of a different kind, an unsafe id, an absent thread, a wrong thread type, and a gateway
216
+ * fault each refuse as a typed `SessionRefusal` too.
217
+ *
218
+ * The authoring tools and the preview tool share this one resolution, thus the two surfaces accept the
219
+ * same thread id and map a load outcome onto a refusal the same way.
220
+ */
221
+ export declare function openReportThread(gateway: ReportSessionStateGateway, scope: Scope): Promise<Result<OpenedThread, SessionRefusal>>;
222
+ /** The eight authoring tools. */
223
+ export interface ReportAuthoringTools {
224
+ readonly add_block: Tool<AddBlockInput, MutationResult>;
225
+ readonly change_block: Tool<ChangeBlockInput, MutationResult>;
226
+ readonly remove_block: Tool<RemoveBlockInput, MutationResult>;
227
+ readonly move_block: Tool<MoveBlockInput, MutationResult>;
228
+ readonly set_title: Tool<SetTitleInput, MutationResult>;
229
+ readonly read_outline: Tool<ReadOutlineInput, OutlineResult>;
230
+ readonly read_block: Tool<ReadBlockInput, ReadBlockResult>;
231
+ readonly finish_draft: Tool<FinishDraftInput, FinishToolResult>;
232
+ }
233
+ /**
234
+ * Make the eight authoring tools over a session-state gateway.
235
+ *
236
+ * Each tool reads the thread id from the scope of the call, and it loads the state of that thread through
237
+ * the gateway. Thus one factory serves every thread, and two threads never share one draft. A mutation
238
+ * persists the new document before it reports `applied: true`, thus a reported landing is never lost.
239
+ */
240
+ export declare function createReportAuthoringTools(gateway: ReportSessionStateGateway): ReportAuthoringTools;
241
+ export {};