@inflexa-ai/harness 0.48.1 → 0.48.2

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 (107) hide show
  1. package/dist/contracts/chat-events.d.ts +40 -0
  2. package/dist/contracts/chat-frame.js +11 -0
  3. package/dist/contracts/index.d.ts +1 -1
  4. package/dist/contracts/message.d.ts +7 -1
  5. package/dist/contracts/schemas/chat-events.d.ts +319 -0
  6. package/dist/contracts/schemas/chat-events.js +29 -0
  7. package/dist/execution/run-synthesis.js +1 -0
  8. package/dist/index.d.ts +3 -1
  9. package/dist/index.js +1 -0
  10. package/dist/input-sanitization.d.ts +30 -2
  11. package/dist/input-sanitization.js +187 -2
  12. package/dist/loop/run-loop.js +50 -5
  13. package/dist/loop/tool-adapter.d.ts +8 -0
  14. package/dist/loop/tool-adapter.js +75 -4
  15. package/dist/loop/tool-detail.d.ts +5 -1
  16. package/dist/loop/tool-detail.js +7 -3
  17. package/dist/loop/tool-value.d.ts +57 -0
  18. package/dist/loop/tool-value.js +249 -0
  19. package/dist/loop/types.d.ts +9 -2
  20. package/dist/memory/conversation-display-storage.js +4 -1
  21. package/dist/memory/count-tokens.js +29 -2
  22. package/dist/sandbox/sandbox-step-translate.d.ts +5 -9
  23. package/dist/sandbox/sandbox-step-translate.js +5 -9
  24. package/dist/tasks/data-profile-activity.d.ts +3 -7
  25. package/dist/tasks/data-profile-activity.js +1 -1
  26. package/dist/tasks/data-profile.js +3 -6
  27. package/dist/tools/ad-hoc-router.js +1 -0
  28. package/dist/tools/bio/alphafold-prediction.js +1 -0
  29. package/dist/tools/bio/chembl.js +1 -0
  30. package/dist/tools/bio/comptox.js +1 -0
  31. package/dist/tools/bio/drug-gene-interactions.js +1 -0
  32. package/dist/tools/bio/gene-disease-evidence.js +1 -0
  33. package/dist/tools/bio/gene-preclinical-profile.js +1 -0
  34. package/dist/tools/bio/lookup-annotation.js +1 -0
  35. package/dist/tools/bio/opentargets.js +1 -0
  36. package/dist/tools/bio/pubchem.js +1 -0
  37. package/dist/tools/bio/pubmed.js +1 -0
  38. package/dist/tools/bio/search-clinical-trials.js +1 -0
  39. package/dist/tools/bio/search-faers.js +1 -0
  40. package/dist/tools/bio/search-gene.js +1 -0
  41. package/dist/tools/bio/search-geo-datasets.js +1 -0
  42. package/dist/tools/bio/search-interactions.js +1 -0
  43. package/dist/tools/bio/search-patents.js +1 -0
  44. package/dist/tools/bio/search-protein.js +1 -0
  45. package/dist/tools/bio/search-regulatory-approvals.js +1 -0
  46. package/dist/tools/bio/target-safety.js +1 -0
  47. package/dist/tools/capability-data.js +5 -0
  48. package/dist/tools/define-tool.d.ts +18 -0
  49. package/dist/tools/define-tool.js +2 -0
  50. package/dist/tools/display/show-user.js +1 -0
  51. package/dist/tools/execute-analysis.js +4 -0
  52. package/dist/tools/knowledge/check.d.ts +47 -1
  53. package/dist/tools/knowledge/check.js +10 -1
  54. package/dist/tools/knowledge/client.d.ts +1 -1
  55. package/dist/tools/knowledge/recommend.js +4 -3
  56. package/dist/tools/knowledge/situation.d.ts +39 -1
  57. package/dist/tools/knowledge/situation.js +80 -8
  58. package/dist/tools/knowledge/skeleton.d.ts +7 -0
  59. package/dist/tools/knowledge/skeleton.js +88 -2
  60. package/dist/tools/knowledge/template.js +1 -0
  61. package/dist/tools/memory/update-working-memory.js +5 -0
  62. package/dist/tools/read-tool-output.js +4 -0
  63. package/dist/tools/report-authoring/authoring-tools.js +8 -0
  64. package/dist/tools/report-session/examine-page.js +5 -0
  65. package/dist/tools/report-session/list-artifacts.js +5 -0
  66. package/dist/tools/report-session/preview-report.js +1 -0
  67. package/dist/tools/report-session/record-version.js +1 -0
  68. package/dist/tools/report-session/script-tools.js +2 -0
  69. package/dist/tools/research/context7-docs.js +2 -0
  70. package/dist/tools/research/generate-analogy-report.js +3 -0
  71. package/dist/tools/research/generate-plan.js +6 -0
  72. package/dist/tools/research/inspect-data-profile.js +5 -0
  73. package/dist/tools/research/inspect-run.js +5 -0
  74. package/dist/tools/research/resolve-citation.js +1 -0
  75. package/dist/tools/research/search-arxiv.js +1 -0
  76. package/dist/tools/research/search-github-repos.js +1 -0
  77. package/dist/tools/research/search-semantic-scholar.js +1 -0
  78. package/dist/tools/research/web-fetch.js +1 -0
  79. package/dist/tools/research/web-search.js +1 -0
  80. package/dist/tools/sandbox/lease-tools.js +2 -0
  81. package/dist/tools/sandbox/link-packages.js +1 -0
  82. package/dist/tools/sandbox/list-available-packages.js +5 -0
  83. package/dist/tools/sandbox/list-available-refs.js +5 -0
  84. package/dist/tools/sandbox/report-blocker.js +1 -0
  85. package/dist/tools/sandbox/skills.js +2 -0
  86. package/dist/tools/sandbox/submit-file-metadata.js +1 -0
  87. package/dist/tools/script/file-tools.js +6 -0
  88. package/dist/tools/script/run-script.js +20 -1
  89. package/dist/tools/start-report-session.js +1 -0
  90. package/dist/tools/tool-view.d.ts +63 -0
  91. package/dist/tools/tool-view.js +68 -0
  92. package/dist/tools/workspace/edit-file.js +5 -0
  93. package/dist/tools/workspace/execute-command.js +5 -2
  94. package/dist/tools/workspace/file-stat.js +5 -0
  95. package/dist/tools/workspace/grep.js +4 -0
  96. package/dist/tools/workspace/list-files.js +5 -0
  97. package/dist/tools/workspace/read-file.js +4 -0
  98. package/dist/tools/workspace/scan-inputs.js +5 -0
  99. package/dist/tools/workspace/show-file.js +1 -0
  100. package/dist/tools/workspace/show-plan.js +1 -0
  101. package/dist/tools/workspace/workspace-search.js +5 -0
  102. package/dist/tools/workspace/write-file.js +7 -2
  103. package/dist/workflows/execute-analysis.js +2 -1
  104. package/dist/workflows/sandbox-step.js +3 -7
  105. package/package.json +1 -1
  106. package/dist/tools/detail-resolver.d.ts +0 -40
  107. package/dist/tools/detail-resolver.js +0 -44
@@ -113,6 +113,7 @@ export const searchGeoDatasetsTool = defineTool({
113
113
  .default(10)
114
114
  .describe("Max datasets to return (default 10, max 50). totalFound reports the true match count regardless."),
115
115
  }),
116
+ view: "none",
116
117
  describeCall: "none",
117
118
  execute: async ({ query, organism, datasetType, limit }) => {
118
119
  let searchQuery = query;
@@ -43,6 +43,7 @@ export const searchInteractionsTool = defineTool({
43
43
  .describe(`Max records returned — partners PER INPUT PROTEIN, network edges, or enriched terms depending on action (default ${DEFAULT_LIMIT}, max 500). ` +
44
44
  "Rows are ordered best-first (score descending for interactions, FDR ascending for enrichment), so the default keeps the strongest."),
45
45
  }),
46
+ view: "none",
46
47
  describeCall: "none",
47
48
  execute: async ({ identifiers, species, action, minScore, limit }) => {
48
49
  if (action === "enrichment") {
@@ -88,6 +88,7 @@ export function createSearchPatentsTool(deps) {
88
88
  "Requires AMASS_API_KEY — a missing key fails terminally: do NOT retry, tell the user the key needs configuring and proceed without " +
89
89
  "Amass patent data. Every call spends the user's Amass credits.",
90
90
  inputSchema,
91
+ view: "none",
91
92
  describeCall: "none",
92
93
  execute: async (input) => {
93
94
  const headers = getAmassHeaders(deps.apiKey);
@@ -49,6 +49,7 @@ export const searchProteinTool = defineTool({
49
49
  .describe(`Max proteins returned (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT}). An exact symbol under reviewedOnly resolves to one entry, so raise ` +
50
50
  "this only for a reviewedOnly=false survey, where a gene carries many TrEMBL rows."),
51
51
  }),
52
+ view: "none",
52
53
  describeCall: ({ query }) => query,
53
54
  execute: async ({ query, organismId, reviewedOnly, limit }) => {
54
55
  const result = await searchProteins(query, {
@@ -108,6 +108,7 @@ export function createSearchRegulatoryApprovalsTool(deps) {
108
108
  "Requires AMASS_API_KEY — a missing key fails terminally: do NOT retry; state that the key is not configured, and proceed without " +
109
109
  "Amass regulatory data. Every call spends the user's Amass credits.",
110
110
  inputSchema,
111
+ view: "none",
111
112
  describeCall: "none",
112
113
  execute: async (input) => {
113
114
  const headers = getAmassHeaders(deps.apiKey);
@@ -107,6 +107,7 @@ export const targetSafetyTool = defineTool({
107
107
  "means no CURATED liability, not that none exists. Both are valid no-data — do not retry. Read `perSource` and each row's `opentargetsNote`: a " +
108
108
  "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.",
109
109
  inputSchema,
110
+ view: "none",
110
111
  describeCall: "none",
111
112
  execute: async ({ identifiers, identifierType, sources, filterOrgan, minSeverity, liabilityLimit }) => {
112
113
  const selected = sources ?? [...SOURCES];
@@ -52,6 +52,11 @@ export function createInspectCapabilityDataTool(deps = {}) {
52
52
  "It takes no arguments and the whole set is small, so one call is the complete answer. " +
53
53
  "An unreadable store answers UNKNOWN rather than absent, so a store that cannot be read never reads as a store that holds nothing.",
54
54
  inputSchema: CapabilityDataInputSchema,
55
+ view: {
56
+ input: (input) => input,
57
+ result: (_input, result) => result,
58
+ error: true,
59
+ },
55
60
  describeCall: () => "declared capability data",
56
61
  execute: async () => {
57
62
  const declared = declaredDataRequirements();
@@ -25,6 +25,7 @@ import type { AgentSession } from "../auth/types.js";
25
25
  import type { AgentRunUsage } from "../loop/metrics.js";
26
26
  import type { EmitFn } from "../loop/types.js";
27
27
  import type { AskRequest } from "./approval/contract.js";
28
+ import type { ToolCallView } from "./tool-view.js";
28
29
  export type { EmitFn } from "../loop/types.js";
29
30
  /**
30
31
  * The error channel of a tool's `Result`. `error` is the message surfaced to
@@ -158,6 +159,8 @@ export type Tool<Input = unknown, Output = unknown> = {
158
159
  describeCall?(input: Input): string | undefined;
159
160
  /** See {@link ToolDefinition.describeResult}. Absent when the tool declares none. */
160
161
  describeResult?(input: Input, result: Output): string | undefined;
162
+ /** See {@link ToolDefinition.view}. Absent when the tool shows no value. */
163
+ view?: ToolCallView<Input, Output>;
161
164
  /**
162
165
  * See {@link ToolDefinition.foldCallRecord}. The record arrives untyped: the
163
166
  * loop passes back only what the `execute` of the same tool attached.
@@ -243,6 +246,21 @@ export type ToolDefinition<Schema extends z.ZodType<z.output<Schema>>, Output, O
243
246
  * The hook never reaches the model.
244
247
  */
245
248
  readonly describeResult?: (input: z.infer<Schema>, result: Output) => string | undefined;
249
+ /**
250
+ * What a call of this tool shows to a host: its input, its result, and its error as values with a kind. A tool
251
+ * shows only what it declares, thus the default of a tool is to show nothing.
252
+ *
253
+ * The decision is REQUIRED. Give a `ToolCallView`, or give the literal `"none"`. `"none"` declares that no call of
254
+ * the tool shows a value. `defineTool` consumes the sentinel at construction, and the sentinel never reaches a
255
+ * packaged tool.
256
+ *
257
+ * A member is synchronous and pure, like a `describeCall` hook, and it never reaches the model. A member gives a
258
+ * string for a `text` value, any other plain value for a `json` value, or the result of `viewText`, `viewJson`,
259
+ * `viewCode`, or `viewFields` for a value of a kind that the author chooses. The emit site then removes the control
260
+ * characters and the listed secret forms, and it cuts a long text. The view is the control for a secret in a form
261
+ * that no rule matches: a tool that holds such data declares a view that leaves it out.
262
+ */
263
+ view: ToolCallView<z.infer<Schema>, Output> | "none";
246
264
  /**
247
265
  * Fold the call record of one settled call into process-local state. The
248
266
  * record is the one that `execute` attached with `withToolCallRecord`.
@@ -135,6 +135,8 @@ export function defineTool(def) {
135
135
  // property check on either side.
136
136
  if (def.describeResult !== undefined)
137
137
  packaged.describeResult = def.describeResult;
138
+ if (def.view !== "none")
139
+ packaged.view = def.view;
138
140
  if (def.ownStep === true)
139
141
  packaged = { ...packaged, ownStep: true };
140
142
  if (def.startsWorkflow === true)
@@ -71,6 +71,7 @@ export const showUserTool = defineTool({
71
71
  "One card per call, rendered in call order; an identical repeat call resolves to the same card, so compose the " +
72
72
  "finished content before calling rather than re-rendering a draft.",
73
73
  inputSchema: ShowUserInputSchema,
74
+ view: "none",
74
75
  describeCall: ({ kind, title }) => (title === undefined ? kind : `${kind}: ${title}`),
75
76
  execute: async (input, ctx) => {
76
77
  // `dataPath` is a reference the host resolves at render time; validate its
@@ -625,6 +625,10 @@ export function createExecuteAnalysisTool(deps) {
625
625
  "A plan that already has an active run returns that run, so a second run of one plan does not start. " +
626
626
  "The launch fails with an error, and no run starts, when the plan is not found or fails validation, when a package of the plan cannot link, or when the run is not authorized.",
627
627
  inputSchema,
628
+ view: {
629
+ input: (input) => input,
630
+ error: true,
631
+ },
628
632
  // The mode is what a user most needs to see here — an approved plan
629
633
  // running is a different event from an ad hoc request being routed.
630
634
  describeCall: (input) => (input.mode === "plan" ? `plan ${input.planId ?? ""}` : `ad hoc: ${input.request ?? ""}`),
@@ -24,6 +24,7 @@ declare const KnowledgeCheckInputSchema: z.ZodObject<{
24
24
  coexpression: "coexpression";
25
25
  deconvolution: "deconvolution";
26
26
  differential_expression: "differential_expression";
27
+ dose_response: "dose_response";
27
28
  enrichment: "enrichment";
28
29
  full_plan: "full_plan";
29
30
  qc: "qc";
@@ -31,9 +32,15 @@ declare const KnowledgeCheckInputSchema: z.ZodObject<{
31
32
  survival: "survival";
32
33
  tf_activity: "tf_activity";
33
34
  }>;
34
- modality: z.ZodLiteral<"bulk_rna_seq">;
35
+ modality: z.ZodEnum<{
36
+ bulk_rna_seq: "bulk_rna_seq";
37
+ microarray: "microarray";
38
+ tempo_seq: "tempo_seq";
39
+ }>;
35
40
  data_state: z.ZodEnum<{
36
41
  counts: "counts";
42
+ de_results: "de_results";
43
+ effect_sizes_only: "effect_sizes_only";
37
44
  fastq: "fastq";
38
45
  log_normalized: "log_normalized";
39
46
  tpm_or_fpkm: "tpm_or_fpkm";
@@ -53,9 +60,12 @@ declare const KnowledgeCheckInputSchema: z.ZodObject<{
53
60
  unknown: "unknown";
54
61
  }>>;
55
62
  organism: z.ZodEnum<{
63
+ dog: "dog";
56
64
  human: "human";
65
+ monkey: "monkey";
57
66
  mouse: "mouse";
58
67
  other: "other";
68
+ rat: "rat";
59
69
  }>;
60
70
  n_groups: z.ZodNumber;
61
71
  n_per_group_min: z.ZodNumber;
@@ -69,6 +79,7 @@ declare const KnowledgeCheckInputSchema: z.ZodObject<{
69
79
  litter: "litter";
70
80
  other: "other";
71
81
  pair: "pair";
82
+ plate: "plate";
72
83
  site: "site";
73
84
  }>>>;
74
85
  block_structure: z.ZodOptional<z.ZodEnum<{
@@ -119,10 +130,12 @@ declare const KnowledgeCheckInputSchema: z.ZodObject<{
119
130
  classifier: z.ZodOptional<z.ZodBoolean>;
120
131
  extra_analyses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
121
132
  annotation: "annotation";
133
+ classifier_call: "classifier_call";
122
134
  clustering: "clustering";
123
135
  coexpression: "coexpression";
124
136
  deconvolution: "deconvolution";
125
137
  pathway_activity: "pathway_activity";
138
+ reference_module_score: "reference_module_score";
126
139
  signature_scoring: "signature_scoring";
127
140
  survival: "survival";
128
141
  tf_activity: "tf_activity";
@@ -145,26 +158,59 @@ declare const KnowledgeCheckInputSchema: z.ZodObject<{
145
158
  outlier_sample: "outlier_sample";
146
159
  sample_identity_doubt: "sample_identity_doubt";
147
160
  }>>>;
161
+ tissue: z.ZodOptional<z.ZodEnum<{
162
+ adipose: "adipose";
163
+ blood: "blood";
164
+ brain: "brain";
165
+ heart: "heart";
166
+ kidney: "kidney";
167
+ liver: "liver";
168
+ muscle: "muscle";
169
+ other: "other";
170
+ }>>;
171
+ exposure_axis: z.ZodOptional<z.ZodEnum<{
172
+ administered_dose: "administered_dose";
173
+ in_vitro_concentration: "in_vitro_concentration";
174
+ systemic_exposure: "systemic_exposure";
175
+ }>>;
176
+ n_dose_levels: z.ZodOptional<z.ZodNumber>;
177
+ n_compounds: z.ZodOptional<z.ZodNumber>;
178
+ control_placement: z.ZodOptional<z.ZodEnum<{
179
+ per_block: "per_block";
180
+ per_time: "per_time";
181
+ per_time_and_block: "per_time_and_block";
182
+ single_baseline: "single_baseline";
183
+ }>>;
184
+ grid_complete: z.ZodOptional<z.ZodBoolean>;
185
+ recovery_group: z.ZodOptional<z.ZodBoolean>;
148
186
  steps: z.ZodArray<z.ZodObject<{
149
187
  step_type: z.ZodEnum<{
150
188
  annotation: "annotation";
189
+ benchmark_dose_sets: "benchmark_dose_sets";
190
+ classifier_call: "classifier_call";
151
191
  clustering: "clustering";
152
192
  coexpression: "coexpression";
153
193
  cohort_assembly: "cohort_assembly";
194
+ compendium_connectivity: "compendium_connectivity";
154
195
  deconvolution: "deconvolution";
155
196
  differential_expression: "differential_expression";
197
+ dose_response_fit: "dose_response_fit";
156
198
  enrichment: "enrichment";
199
+ exposure_margin: "exposure_margin";
157
200
  filter_low_counts: "filter_low_counts";
158
201
  model_design: "model_design";
159
202
  multiple_testing: "multiple_testing";
160
203
  normalize: "normalize";
161
204
  pathway_activity: "pathway_activity";
205
+ point_of_departure: "point_of_departure";
162
206
  qc_sample_structure: "qc_sample_structure";
207
+ reference_module_score: "reference_module_score";
163
208
  report: "report";
164
209
  shrink_lfc: "shrink_lfc";
165
210
  signature_scoring: "signature_scoring";
166
211
  survival: "survival";
167
212
  tf_activity: "tf_activity";
213
+ trajectory_classification: "trajectory_classification";
168
214
  transcript_level: "transcript_level";
169
215
  variance_partition: "variance_partition";
170
216
  }>;
@@ -23,6 +23,14 @@ const DraftedStepSchema = z.object({
23
23
  "differential_expression",
24
24
  "shrink_lfc",
25
25
  "multiple_testing",
26
+ "dose_response_fit",
27
+ "benchmark_dose_sets",
28
+ "point_of_departure",
29
+ "exposure_margin",
30
+ "trajectory_classification",
31
+ "reference_module_score",
32
+ "classifier_call",
33
+ "compendium_connectivity",
26
34
  "enrichment",
27
35
  "variance_partition",
28
36
  "tf_activity",
@@ -67,7 +75,7 @@ export function createKnowledgeCheckTool(deps) {
67
75
  let calls = 0;
68
76
  return defineTool({
69
77
  id: "knowledge_check",
70
- description: "Check the method steps you drafted for a bulk RNA-seq analysis against the rules of the Inflexa knowledge service, once, after the draft and before `submit_plan`. " +
78
+ description: "Check the method steps you drafted for an expression analysis against the rules of the Inflexa knowledge service, once, after the draft and before `submit_plan`. " +
71
79
  "Send the same situation you sent to `knowledge_recommend` and the drafted steps: the step type, the method as the step names it, its package, and its key parameters. " +
72
80
  "The answer lists `violations` (a forbidden method, or an inferential test on a design that a flag limits) and `warnings` (a method outside the set the rules name, a parameter that differs from a sourced default, or a required parameter that the step does not state), each with the rule id and the permitted alternatives. " +
73
81
  "Revise a violated step once, then submit. A warning is advice: keep the step when the data gives a reason, and state the reason in the step. `ok: true` means no finding on the assessed steps. " +
@@ -75,6 +83,7 @@ export function createKnowledgeCheckTool(deps) {
75
83
  "`match: unavailable` means the service did not answer, and `match: snapshot_mismatch` means the service moved to another release than the one your recommend answer came from; in both cases submit the draft as it is. " +
76
84
  `The host accepts ${CHECK_CALL_LIMIT} checks per plan; after that the tool refuses and you submit with the findings you have.`,
77
85
  inputSchema: KnowledgeCheckInputSchema,
86
+ view: "none",
78
87
  describeCall: ({ steps }) => `${steps.length} drafted step(s)`,
79
88
  describeResult: (_input, result) => ("ok" in result ? describeFindings(result) : result.match),
80
89
  execute: async (input) => {
@@ -20,7 +20,7 @@
20
20
  import { z } from "zod";
21
21
  import type { SituationFieldsSchema } from "./situation.js";
22
22
  /** The role of the blocking factor. A role and never a column name, because the situation leaves the machine. */
23
- export type BlockingRole = "individual" | "cell_line" | "litter" | "site" | "pair" | "other";
23
+ export type BlockingRole = "individual" | "cell_line" | "litter" | "site" | "pair" | "plate" | "other";
24
24
  /** The role of a covariate of the design. A role and never a column name. */
25
25
  export type CovariateRole = "sex" | "age" | "clinical" | "technical" | "other";
26
26
  /** The role of the numeric variable of interest when the question is a trend, not a comparison of groups. */
@@ -49,10 +49,10 @@ export function toPlannerAnswer(joined) {
49
49
  export function createKnowledgeRecommendTool(deps) {
50
50
  return defineTool({
51
51
  id: "knowledge_recommend",
52
- description: "Get the cited, versioned procedure for a bulk RNA-seq analysis situation from the Inflexa knowledge service, folded into plan steps: " +
53
- "which method for each step (QC, filter, normalize, model, test, shrinkage, multiple testing, enrichment, report), " +
52
+ description: "Get the cited, versioned procedure for an expression analysis situation (bulk RNA-seq, a microarray, or a TempO-Seq panel) from the Inflexa knowledge service, folded into plan steps: " +
53
+ "which method for each step (for example QC, filter, normalize, model, test, shrinkage, multiple testing, enrichment, dose-response fit, point of departure, report), " +
54
54
  "its settings with their sources, a tested script template per step, the rules that select them, and the evidence. " +
55
- "Call it BEFORE you draft a method step for bulk RNA-seq differential expression, enrichment, or QC. " +
55
+ "Call it BEFORE you draft a method step for a differential expression, an enrichment, a QC, a dose response, or any other question that `question` names. " +
56
56
  "Fill the situation from the Data Context (the design, the replicate counts, the batch structure, the data state, the quality concerns); never send a sample name or a file path. " +
57
57
  `Example input: ${SITUATION_EXAMPLE}. ` +
58
58
  "The answer holds one representation: `plan_skeleton`, the plan steps with the id, the name, the track, the agent, the packages, the dependencies, " +
@@ -80,6 +80,7 @@ export function createKnowledgeRecommendTool(deps) {
80
80
  "That is the environment answer; do not call the listing tools for a package or a collection the answer already reports. " +
81
81
  "Call it once per situation; a second call with the same situation gives the same answer.",
82
82
  inputSchema: SituationFieldsSchema,
83
+ view: "none",
83
84
  describeCall: ({ question, n_per_group_min, n_per_group_max, batch }) => `${question}: ${n_per_group_min}-${n_per_group_max} per group, batch ${batch}`,
84
85
  describeResult: (_input, result) => result.match === "applicable" ? `${result.plan_skeleton.length} steps, ${result.claims.length} claims` : result.match,
85
86
  execute: async (input, ctx) => {
@@ -12,6 +12,7 @@ export declare const SituationFieldsSchema: z.ZodObject<{
12
12
  coexpression: "coexpression";
13
13
  deconvolution: "deconvolution";
14
14
  differential_expression: "differential_expression";
15
+ dose_response: "dose_response";
15
16
  enrichment: "enrichment";
16
17
  full_plan: "full_plan";
17
18
  qc: "qc";
@@ -19,9 +20,15 @@ export declare const SituationFieldsSchema: z.ZodObject<{
19
20
  survival: "survival";
20
21
  tf_activity: "tf_activity";
21
22
  }>;
22
- modality: z.ZodLiteral<"bulk_rna_seq">;
23
+ modality: z.ZodEnum<{
24
+ bulk_rna_seq: "bulk_rna_seq";
25
+ microarray: "microarray";
26
+ tempo_seq: "tempo_seq";
27
+ }>;
23
28
  data_state: z.ZodEnum<{
24
29
  counts: "counts";
30
+ de_results: "de_results";
31
+ effect_sizes_only: "effect_sizes_only";
25
32
  fastq: "fastq";
26
33
  log_normalized: "log_normalized";
27
34
  tpm_or_fpkm: "tpm_or_fpkm";
@@ -41,9 +48,12 @@ export declare const SituationFieldsSchema: z.ZodObject<{
41
48
  unknown: "unknown";
42
49
  }>>;
43
50
  organism: z.ZodEnum<{
51
+ dog: "dog";
44
52
  human: "human";
53
+ monkey: "monkey";
45
54
  mouse: "mouse";
46
55
  other: "other";
56
+ rat: "rat";
47
57
  }>;
48
58
  n_groups: z.ZodNumber;
49
59
  n_per_group_min: z.ZodNumber;
@@ -57,6 +67,7 @@ export declare const SituationFieldsSchema: z.ZodObject<{
57
67
  litter: "litter";
58
68
  other: "other";
59
69
  pair: "pair";
70
+ plate: "plate";
60
71
  site: "site";
61
72
  }>>>;
62
73
  block_structure: z.ZodOptional<z.ZodEnum<{
@@ -107,10 +118,12 @@ export declare const SituationFieldsSchema: z.ZodObject<{
107
118
  classifier: z.ZodOptional<z.ZodBoolean>;
108
119
  extra_analyses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
109
120
  annotation: "annotation";
121
+ classifier_call: "classifier_call";
110
122
  clustering: "clustering";
111
123
  coexpression: "coexpression";
112
124
  deconvolution: "deconvolution";
113
125
  pathway_activity: "pathway_activity";
126
+ reference_module_score: "reference_module_score";
114
127
  signature_scoring: "signature_scoring";
115
128
  survival: "survival";
116
129
  tf_activity: "tf_activity";
@@ -133,6 +146,31 @@ export declare const SituationFieldsSchema: z.ZodObject<{
133
146
  outlier_sample: "outlier_sample";
134
147
  sample_identity_doubt: "sample_identity_doubt";
135
148
  }>>>;
149
+ tissue: z.ZodOptional<z.ZodEnum<{
150
+ adipose: "adipose";
151
+ blood: "blood";
152
+ brain: "brain";
153
+ heart: "heart";
154
+ kidney: "kidney";
155
+ liver: "liver";
156
+ muscle: "muscle";
157
+ other: "other";
158
+ }>>;
159
+ exposure_axis: z.ZodOptional<z.ZodEnum<{
160
+ administered_dose: "administered_dose";
161
+ in_vitro_concentration: "in_vitro_concentration";
162
+ systemic_exposure: "systemic_exposure";
163
+ }>>;
164
+ n_dose_levels: z.ZodOptional<z.ZodNumber>;
165
+ n_compounds: z.ZodOptional<z.ZodNumber>;
166
+ control_placement: z.ZodOptional<z.ZodEnum<{
167
+ per_block: "per_block";
168
+ per_time: "per_time";
169
+ per_time_and_block: "per_time_and_block";
170
+ single_baseline: "single_baseline";
171
+ }>>;
172
+ grid_complete: z.ZodOptional<z.ZodBoolean>;
173
+ recovery_group: z.ZodOptional<z.ZodBoolean>;
136
174
  }, z.core.$strip>;
137
175
  export type SituationFields = z.infer<typeof SituationFieldsSchema>;
138
176
  /** The situation as the client sends it: the fields as given, with the caller preference held out of the situation fields. */
@@ -18,16 +18,28 @@ export const SituationFieldsSchema = z.object({
18
18
  "clustering",
19
19
  "survival",
20
20
  "signature_scoring",
21
+ "dose_response",
21
22
  ])
22
23
  .describe("What the plan needs a procedure for. `full_plan` returns QC, filtering, the model, the test, shrinkage, multiple testing, enrichment, and the report in one answer. " +
23
24
  "Use `differential_expression` when the research question does not ask for pathways or gene sets; an enrichment step the user did not ask for is unasked scope. " +
24
25
  "Use `enrichment` when a results table is the input and no new test is fitted, and `qc` when the question stops at the sample structure. " +
25
26
  "Use `tf_activity` for regulator or pathway activity, `deconvolution` for cell type proportions, `coexpression` for modules, `clustering` for sample subgroups, `survival` for an outcome association, " +
26
- "and `signature_scoring` for a per-sample score, or with `classifier: true` for a classifier of the samples."),
27
- modality: z.literal("bulk_rna_seq").describe("The assay. Phase 0 serves bulk RNA-seq only."),
27
+ "and `signature_scoring` for a per-sample score, or with `classifier: true` for a classifier of the samples. " +
28
+ "Use `dose_response` when the question asks at which exposure the expression leaves the control: the walk fits a curve per gene over the dose, takes the benchmark dose of each gene set, " +
29
+ "reads the transcriptomic point of departure from the lowest set median, and divides it by an efficacious exposure for the exposure margin. " +
30
+ "A `dose_response` situation carries `n_dose_levels`, `tissue`, and `exposure_axis`, because the fit, the point of departure, and the margin each stop without one of them, " +
31
+ "and it carries `control_placement`, `grid_complete`, `recovery_group`, and `n_compounds` when the design holds them."),
32
+ modality: z
33
+ .enum(["bulk_rna_seq", "microarray", "tempo_seq"])
34
+ .describe("The platform that measured the expression: `bulk_rna_seq` for a sequencing run over the whole transcriptome; `microarray` for the probe intensities of a hybridization array, the platform of the legacy toxicogenomics compendia; " +
35
+ "`tempo_seq` for the targeted read counts of a TempO-Seq probe panel, where the panel and not the transcriptome is the gene universe. " +
36
+ "An array reads no sequence, thus it has no read count and no library size: a `microarray` takes `data_state` `log_normalized`, `de_results`, or `effect_sizes_only`, and `fastq`, `counts`, or `tpm_or_fpkm` stops the plan as a contradiction. " +
37
+ "A probe panel is a sequencing assay and it keeps the read count, thus `tempo_seq` takes `counts`; a panel gives no transcript length, thus never `tpm_or_fpkm`."),
28
38
  data_state: z
29
- .enum(["fastq", "counts", "tpm_or_fpkm", "log_normalized"])
30
- .describe("The state of the expression data in the inputs: raw reads, a raw integer count matrix, TPM/FPKM/RPKM abundances, or log-scale normalized values (log-CPM, microarray)."),
39
+ .enum(["fastq", "counts", "tpm_or_fpkm", "log_normalized", "de_results", "effect_sizes_only"])
40
+ .describe("The state of the expression data in the inputs: raw reads, a raw integer count matrix, TPM/FPKM/RPKM abundances, or log-scale normalized values (log-CPM, microarray). " +
41
+ "`de_results` is a table of contrasts with an effect size and a p-value per gene, and `effect_sizes_only` the same table with no p-value. " +
42
+ "A table of contrasts holds no value of a sample and no variance inside a dose group: without the p-values no step counts calls at a false discovery rate, and either table stops a benchmark dose fit."),
31
43
  count_source: z
32
44
  .enum(["salmon", "kallisto", "star_featurecounts", "rsem", "unknown"])
33
45
  .optional()
@@ -41,7 +53,9 @@ export const SituationFieldsSchema = z.object({
41
53
  "`corrected_counts`: a table described as lengthScaledTPM or scaledTPM counts. " +
42
54
  "`integer_counts`: integer counts from an aligner and a counter (STAR, featureCounts) or from a 3' protocol, with no length data. " +
43
55
  "`unknown`: a count table from a quantifier with no statement of the correction. Omit when `data_state` is not `counts`."),
44
- organism: z.enum(["human", "mouse", "other"]).describe("The organism of the samples."),
56
+ organism: z
57
+ .enum(["human", "mouse", "rat", "dog", "monkey", "other"])
58
+ .describe("The organism of the samples. `rat`, `dog`, and `monkey` are the species of a regulatory toxicology study; use `other` only for a species this list does not name."),
45
59
  n_groups: z
46
60
  .number()
47
61
  .int()
@@ -71,10 +85,10 @@ export const SituationFieldsSchema = z.object({
71
85
  .describe("For a survival question, the number of samples with an observed event (event indicator 1) in the outcome column. Omit when the question is not survival or the count is not in the profile."),
72
86
  paired: z.boolean().describe("True when each subject contributes a sample to more than one condition (paired or repeated measures)."),
73
87
  blocking_factor: z
74
- .enum(["individual", "cell_line", "litter", "site", "pair", "other"])
88
+ .enum(["individual", "cell_line", "litter", "site", "pair", "plate", "other"])
75
89
  .nullable()
76
90
  .optional()
77
- .describe("The role of the blocking factor that is not the condition, never its column name: `individual` when the same subject, donor, or animal gives more than one sample; `cell_line`; `litter`; `site` for a center or a lab of a multi-site study; `pair` for matched pairs of different individuals; `other`. Omit when there is none. The column name stays in the step."),
91
+ .describe("The role of the blocking factor that is not the condition, never its column name: `individual` when the same subject, donor, or animal gives more than one sample; `cell_line`; `litter`; `site` for a center or a lab of a multi-site study; `pair` for matched pairs of different individuals; `plate` when the samples sit on culture or assay plates and one plate holds a set of them; `other`. Omit when there is none. The column name stays in the step."),
78
92
  block_structure: z
79
93
  .enum(["crossed", "nested"])
80
94
  .optional()
@@ -126,10 +140,14 @@ export const SituationFieldsSchema = z.object({
126
140
  "survival",
127
141
  "transcript_level",
128
142
  "annotation",
143
+ "reference_module_score",
144
+ "classifier_call",
129
145
  ]))
130
146
  .optional()
131
147
  .describe("Analyses the research question asks for beside the question kind, for example a regulator activity beside a differential expression. Each joins the procedure at its place. " +
132
- "Add `annotation` when the rows of the count table are not gene symbols (Ensembl or Entrez identifiers) and a later step needs symbols: the identifier map is then a vetted step with its own template, and no other step rebuilds it. Omit when none."),
148
+ "Add `annotation` when the rows of the count table are not gene symbols (Ensembl or Entrez identifiers) and a later step needs symbols: the identifier map is then a vetted step with its own template, and no other step rebuilds it. " +
149
+ "Add `reference_module_score` for a score of each sample against a reference co-expression module set, and `classifier_call` for the call of a validated classifier on each sample. " +
150
+ "Each of the two contrasts two levels of the group column, thus either one needs two or more groups, a time course, or a numeric predictor. Omit when none."),
133
151
  preferred_language: z
134
152
  .enum(["R", "python"])
135
153
  .optional()
@@ -142,6 +160,60 @@ export const SituationFieldsSchema = z.object({
142
160
  .array(z.enum(["low_depth_sample", "outlier_sample", "sample_identity_doubt", "high_duplication", "degraded_rna"]))
143
161
  .optional()
144
162
  .describe("The quality concerns of the profile as typed flags: a sample with a library size far below the median; a sample that does not cluster with its group; a doubt about the identity of a sample; high duplication; degraded RNA (a low RNA integrity number, a 3' coverage bias, or formalin-fixed material)."),
163
+ tissue: z
164
+ .enum(["liver", "kidney", "heart", "muscle", "brain", "adipose", "blood", "other"])
165
+ .optional()
166
+ .describe("The tissue the samples come from: `liver`, `kidney`, `heart`, `muscle` for skeletal muscle, `brain` whole or one region, `adipose`, `blood` whole or a fraction, " +
167
+ "or `other` for a tissue this list does not name, for example a cell line. " +
168
+ "A transcriptomic point of departure belongs to the tissue of its samples, and the point of departure of one tissue says nothing about another, thus one call covers one tissue and the exposure margin stops without this field. " +
169
+ "Read it from the sample description of the profile or from the user request. Omit it when the question is not `dose_response`, or when the samples are not from one tissue."),
170
+ exposure_axis: z
171
+ .enum(["administered_dose", "in_vitro_concentration", "systemic_exposure"])
172
+ .optional()
173
+ .describe("The axis on which the exposure values of the study lie: `administered_dose` for the dose the study gives to the animal or to the culture, for example in mg/kg; " +
174
+ "`in_vitro_concentration` for the concentration of the compound in the medium of a cell culture; `systemic_exposure` for a measured level in the plasma, for example a Cmax or an AUC. " +
175
+ "The exposure margin divides the point of departure by an efficacious exposure, and that quotient means something only when both values lie on one axis and in one unit. " +
176
+ "Thus the margin stops without this field, and `in_vitro_concentration` stops it too, because a concentration in a medium is no dose of an intact organism and the extrapolation from in vitro to in vivo comes first. " +
177
+ "Read it from the dose column of the profile or from the user request. Omit it when the question is not `dose_response`."),
178
+ n_dose_levels: z
179
+ .number()
180
+ .int()
181
+ .min(1)
182
+ .optional()
183
+ .describe("The number of dose levels of the design, the control level included. The dose levels are groups of the design, thus this count is at most `n_groups`, " +
184
+ "unless `continuous_predictor` is `dose` and the dose itself, not a group, is the factor of interest. " +
185
+ "Three levels is the floor of the walk: the model families of the average hold three or more parameters, thus below three levels no gene and no gene set has a benchmark dose and the point of departure stops. " +
186
+ "Read it from the dose column of the profile. Omit it when the question is not `dose_response`."),
187
+ n_compounds: z
188
+ .number()
189
+ .int()
190
+ .min(1)
191
+ .optional()
192
+ .describe("The number of compounds the study exposes the samples to. One call covers one compound: no design term names a compound as a factor, and the point of departure and the exposure margin each give one value for one compound. " +
193
+ "Thus 2 or more says that the plan runs the walk one time for each compound, each run over the samples of that compound and the control samples of that compound, and it pools no two compounds in one fit. " +
194
+ "Read it from the sample table of the profile. Omit it when the question is not `dose_response`."),
195
+ control_placement: z
196
+ .enum(["single_baseline", "per_time", "per_block", "per_time_and_block"])
197
+ .optional()
198
+ .describe("Where the control samples sit in the design, which decides the samples that form the contrast of each treated sample: `single_baseline` when one control group serves every treated group; " +
199
+ "`per_time` when each time point holds its own control group; `per_block` when each block, for example a plate, holds its own control group; `per_time_and_block` for both. " +
200
+ "With a control at each time the design takes the dose and the time as fixed factors and the control of the same time as the reference level, so that a time effect all dose levels share leaves the dose contrast. " +
201
+ "`per_time` and `per_time_and_block` need `n_timepoints` or `continuous_predictor: time`, and `per_block` and `per_time_and_block` need `blocking_factor`. " +
202
+ "Read it from the sample table of the profile. Omit it when the question is not `dose_response`."),
203
+ grid_complete: z
204
+ .boolean()
205
+ .optional()
206
+ .describe("Whether every cell of the grid of the dose levels, the times, and the blocks holds at least one sample. Set false when a cell holds none: a model that assumes every cell, " +
207
+ "for example a cell-means model with one coefficient per combination or a full interaction of the factors, then carries a coefficient with no data and the design matrix loses rank. " +
208
+ "The plan then fits the terms the design can estimate and states the estimable contrasts only. " +
209
+ "Read it from the sample table of the profile. Omit it when every cell holds a sample."),
210
+ recovery_group: z
211
+ .boolean()
212
+ .optional()
213
+ .describe("Set true when the study holds a group that it observes after the exposure ends, for example animals that rest after the last dose. " +
214
+ "Such a group carries no dose at the time of its sample, thus it lies at no point of the dose-response curve and the fit leaves it out; " +
215
+ "a recovery sample placed at the dose of its exposure makes the response of that dose lower than the exposure gives. " +
216
+ "Read it from the time column or the group labels of the sample table. Omit it when the study samples every group during the exposure."),
145
217
  });
146
218
  /** The situation as the client sends it: the fields as given, with the caller preference held out of the situation fields. */
147
219
  export function toSituation(fields) {
@@ -19,6 +19,13 @@
19
19
  * one enrichment step on its own track, and one report step. A group with
20
20
  * no step in the procedure is absent from the skeleton.
21
21
  *
22
+ * A step whose own template reads the output of another template gets its
23
+ * own group, because a fold keeps the template of the central step alone and
24
+ * it would drop the others. Thus the four steps of the dose-response chain
25
+ * (the benchmark dose of each gene, the benchmark dose of each gene set, the
26
+ * point of departure, and the exposure margin) are four groups of one track,
27
+ * in that order, and each of the other four new steps is a group of its own.
28
+ *
22
29
  * The central step of a group names the method of the skeleton step, and it
23
30
  * gives the alternatives, the template, and the environment: an alternative
24
31
  * of a folded step (a normalization in the differential expression group)