@inflexa-ai/harness 0.48.0 → 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.
- package/dist/contracts/chat-events.d.ts +40 -0
- package/dist/contracts/chat-frame.js +11 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/message.d.ts +7 -1
- package/dist/contracts/schemas/chat-events.d.ts +319 -0
- package/dist/contracts/schemas/chat-events.js +29 -0
- package/dist/execution/run-synthesis.js +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -1
- package/dist/input-sanitization.d.ts +30 -2
- package/dist/input-sanitization.js +187 -2
- package/dist/loop/run-loop.js +50 -5
- package/dist/loop/tool-adapter.d.ts +8 -0
- package/dist/loop/tool-adapter.js +75 -4
- package/dist/loop/tool-detail.d.ts +5 -1
- package/dist/loop/tool-detail.js +7 -3
- package/dist/loop/tool-value.d.ts +57 -0
- package/dist/loop/tool-value.js +249 -0
- package/dist/loop/types.d.ts +9 -2
- package/dist/memory/conversation-display-storage.js +4 -1
- package/dist/memory/count-tokens.js +29 -2
- package/dist/runtime/dbos.d.ts +6 -0
- package/dist/runtime/dbos.js +14 -2
- package/dist/sandbox/sandbox-step-translate.d.ts +5 -9
- package/dist/sandbox/sandbox-step-translate.js +5 -9
- package/dist/state/artifacts.d.ts +4 -1
- package/dist/state/artifacts.js +4 -1
- package/dist/state/index.d.ts +2 -2
- package/dist/state/index.js +1 -1
- package/dist/state/plans.d.ts +27 -0
- package/dist/state/plans.js +57 -3
- package/dist/tasks/data-profile-activity.d.ts +3 -7
- package/dist/tasks/data-profile-activity.js +1 -1
- package/dist/tasks/data-profile.js +3 -6
- package/dist/tools/ad-hoc-router.js +1 -0
- package/dist/tools/bio/alphafold-prediction.js +1 -0
- package/dist/tools/bio/chembl.js +1 -0
- package/dist/tools/bio/comptox.js +1 -0
- package/dist/tools/bio/drug-gene-interactions.js +1 -0
- package/dist/tools/bio/gene-disease-evidence.js +1 -0
- package/dist/tools/bio/gene-preclinical-profile.js +1 -0
- package/dist/tools/bio/lookup-annotation.js +1 -0
- package/dist/tools/bio/opentargets.js +1 -0
- package/dist/tools/bio/pubchem.js +1 -0
- package/dist/tools/bio/pubmed.js +1 -0
- package/dist/tools/bio/search-clinical-trials.js +1 -0
- package/dist/tools/bio/search-faers.js +1 -0
- package/dist/tools/bio/search-gene.js +1 -0
- package/dist/tools/bio/search-geo-datasets.js +1 -0
- package/dist/tools/bio/search-interactions.js +1 -0
- package/dist/tools/bio/search-patents.js +1 -0
- package/dist/tools/bio/search-protein.js +1 -0
- package/dist/tools/bio/search-regulatory-approvals.js +1 -0
- package/dist/tools/bio/target-safety.js +1 -0
- package/dist/tools/capability-data.js +5 -0
- package/dist/tools/define-tool.d.ts +18 -0
- package/dist/tools/define-tool.js +2 -0
- package/dist/tools/display/show-user.js +1 -0
- package/dist/tools/execute-analysis.js +4 -0
- package/dist/tools/knowledge/check.d.ts +47 -1
- package/dist/tools/knowledge/check.js +10 -1
- package/dist/tools/knowledge/client.d.ts +1 -1
- package/dist/tools/knowledge/recommend.js +4 -3
- package/dist/tools/knowledge/situation.d.ts +39 -1
- package/dist/tools/knowledge/situation.js +80 -8
- package/dist/tools/knowledge/skeleton.d.ts +7 -0
- package/dist/tools/knowledge/skeleton.js +88 -2
- package/dist/tools/knowledge/template.js +1 -0
- package/dist/tools/memory/update-working-memory.js +5 -0
- package/dist/tools/read-tool-output.js +4 -0
- package/dist/tools/report-authoring/authoring-tools.js +8 -0
- package/dist/tools/report-session/examine-page.js +5 -0
- package/dist/tools/report-session/list-artifacts.js +5 -0
- package/dist/tools/report-session/preview-report.js +1 -0
- package/dist/tools/report-session/record-version.js +1 -0
- package/dist/tools/report-session/script-tools.js +2 -0
- package/dist/tools/research/context7-docs.js +2 -0
- package/dist/tools/research/generate-analogy-report.js +3 -0
- package/dist/tools/research/generate-plan.js +6 -0
- package/dist/tools/research/inspect-data-profile.js +5 -0
- package/dist/tools/research/inspect-run.js +5 -0
- package/dist/tools/research/resolve-citation.js +1 -0
- package/dist/tools/research/search-arxiv.js +1 -0
- package/dist/tools/research/search-github-repos.js +1 -0
- package/dist/tools/research/search-semantic-scholar.js +1 -0
- package/dist/tools/research/web-fetch.js +1 -0
- package/dist/tools/research/web-search.js +1 -0
- package/dist/tools/sandbox/lease-tools.js +2 -0
- package/dist/tools/sandbox/link-packages.js +1 -0
- package/dist/tools/sandbox/list-available-packages.js +5 -0
- package/dist/tools/sandbox/list-available-refs.js +5 -0
- package/dist/tools/sandbox/report-blocker.js +1 -0
- package/dist/tools/sandbox/skills.js +2 -0
- package/dist/tools/sandbox/submit-file-metadata.js +1 -0
- package/dist/tools/script/file-tools.js +6 -0
- package/dist/tools/script/run-script.js +20 -1
- package/dist/tools/start-report-session.js +1 -0
- package/dist/tools/tool-view.d.ts +63 -0
- package/dist/tools/tool-view.js +68 -0
- package/dist/tools/workspace/edit-file.js +5 -0
- package/dist/tools/workspace/execute-command.js +5 -2
- package/dist/tools/workspace/file-stat.js +5 -0
- package/dist/tools/workspace/grep.js +4 -0
- package/dist/tools/workspace/list-files.js +5 -0
- package/dist/tools/workspace/read-file.js +4 -0
- package/dist/tools/workspace/scan-inputs.js +5 -0
- package/dist/tools/workspace/show-file.js +1 -0
- package/dist/tools/workspace/show-plan.js +1 -0
- package/dist/tools/workspace/workspace-search.js +5 -0
- package/dist/tools/workspace/write-file.js +7 -2
- package/dist/workflows/execute-analysis.js +2 -1
- package/dist/workflows/sandbox-step.js +3 -7
- package/package.json +1 -1
- package/dist/tools/detail-resolver.d.ts +0 -40
- package/dist/tools/detail-resolver.js +0 -44
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
* no terminal at all.
|
|
21
21
|
*/
|
|
22
22
|
import type { Logger } from "../lib/logger.js";
|
|
23
|
-
import type { ToolDetailResolver } from "../tools/detail-resolver.js";
|
|
24
23
|
/**
|
|
25
24
|
* Writes one part to the run-event stream. Injected rather than imported so this
|
|
26
25
|
* module stays free of the durability engine and so a test can record what a
|
|
@@ -64,15 +63,12 @@ export type ProfileActivityEmitter = {
|
|
|
64
63
|
* Reported per tool call the profiler agent makes, phrased by the shared
|
|
65
64
|
* translator.
|
|
66
65
|
*
|
|
67
|
-
* `
|
|
68
|
-
*
|
|
69
|
-
* call rather than held by the emitter because the emitter exists before the
|
|
70
|
-
* agent does — `sandboxInit` is reported before the sandbox the agent's tools
|
|
71
|
-
* are bound to. The call site supplies a RESOLVER, never a phrase: the
|
|
66
|
+
* `detail` is the detail of the loop event, which the `describeCall` hook of
|
|
67
|
+
* the called tool made. The call site supplies a detail, never a phrase: the
|
|
72
68
|
* phrases are this capability's observable contract, and a call site that
|
|
73
69
|
* composed its own is a call site that can drift from it.
|
|
74
70
|
*/
|
|
75
|
-
forTool(toolUseId: string, name: string,
|
|
71
|
+
forTool(toolUseId: string, name: string, detail?: string): Promise<void>;
|
|
76
72
|
/** Reported for the vector-store pass that indexes per-file descriptions. */
|
|
77
73
|
indexing(): Promise<void>;
|
|
78
74
|
/** The success terminal. Emitted only after the terminal ledger write has landed. */
|
|
@@ -58,7 +58,7 @@ export function createProfileActivityEmitter(write, frame, logger, now) {
|
|
|
58
58
|
sandboxInit: () => emit("sandbox-init", "Starting sandbox"),
|
|
59
59
|
scanning: () => emit("executing", "Scanning input files"),
|
|
60
60
|
agentStarting: () => emit("executing", "Running data-profiler"),
|
|
61
|
-
forTool: (toolUseId, name,
|
|
61
|
+
forTool: (toolUseId, name, detail) => emit("executing", activityForTool(name, detail), toolUseId),
|
|
62
62
|
indexing: () => emit("indexing", "Indexing input descriptions for search"),
|
|
63
63
|
complete: () => emit("complete", "Profile complete"),
|
|
64
64
|
failed: (reason) => emit("failed", reason),
|
|
@@ -27,13 +27,12 @@ import { forStep, forSubAgent } from "../auth/types.js";
|
|
|
27
27
|
import { presentFirst } from "../lib/present.js";
|
|
28
28
|
import { collapseWhitespace } from "../lib/text.js";
|
|
29
29
|
import { createDataProfilerAgent } from "../agents/sandbox/data-profiler.js";
|
|
30
|
-
import { runWithSalvage
|
|
30
|
+
import { runWithSalvage } from "../loop/run-loop.js";
|
|
31
31
|
import { recordedInputOf } from "../loop/tool-adapter.js";
|
|
32
32
|
import { createNoopLogger } from "../lib/console-logger.js";
|
|
33
33
|
import { deliverNotice, passGate } from "../lib/hooks.js";
|
|
34
34
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
35
35
|
import { defineTool } from "../tools/define-tool.js";
|
|
36
|
-
import { createDetailResolver } from "../tools/detail-resolver.js";
|
|
37
36
|
import { renderResources, renderWorkspace } from "../prompts/briefing.js";
|
|
38
37
|
import { workPause } from "../workflows/pause.js";
|
|
39
38
|
import { clearPausesOfWork } from "../state/paused-work.js";
|
|
@@ -109,6 +108,7 @@ export function createSubmitProfileTool(detected, menu) {
|
|
|
109
108
|
"A submission whose operations do not resolve, or that leaves kept files unclaimed, comes back with " +
|
|
110
109
|
"the errors; the repair is a FULL resubmit of the whole operation list. The last accepted submission is the profile.",
|
|
111
110
|
inputSchema: ProfileSubmissionSchema,
|
|
111
|
+
view: "none",
|
|
112
112
|
describeCall: "none",
|
|
113
113
|
execute: (input) => {
|
|
114
114
|
const resolution = resolveProfileSubmission(input, detected, menu, { finalRound: false });
|
|
@@ -545,9 +545,6 @@ export async function runDataProfileBody(input, deps) {
|
|
|
545
545
|
const submitProfileTool = createSubmitProfileTool(detected, briefingMenu);
|
|
546
546
|
const baseAgent = createDataProfilerAgent(sandboxAgentDeps);
|
|
547
547
|
const agentDef = { ...baseAgent, tools: [...baseAgent.tools, submitProfileTool] };
|
|
548
|
-
// The profiler's own roster, `submit_profile` included, so every call it
|
|
549
|
-
// makes is phrased by the tool that was called.
|
|
550
|
-
const resolveDetail = createDetailResolver([...withRunScript(agentDef).tools, ...(agentDef.scriptTools ?? [])], logger);
|
|
551
548
|
const signal = new AbortController().signal;
|
|
552
549
|
// Covers the gap between a ready sandbox and the agent's first tool call, which would
|
|
553
550
|
// otherwise still read as `Starting sandbox` long after the container was up. The run
|
|
@@ -563,7 +560,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
563
560
|
// happening now. The events of a tool call arrive from inside its step.
|
|
564
561
|
emit: async (event) => {
|
|
565
562
|
if (event.type === "tool-started")
|
|
566
|
-
await activity.forTool(event.toolUseId, event.name, event.
|
|
563
|
+
await activity.forTool(event.toolUseId, event.name, event.detail);
|
|
567
564
|
},
|
|
568
565
|
invocationId: workflowId,
|
|
569
566
|
usageRecorder: deps.usageRecorder,
|
|
@@ -125,6 +125,7 @@ function routeTool() {
|
|
|
125
125
|
id: SUBMIT_ROUTE_TOOL_ID,
|
|
126
126
|
description: "Submit the single specialist and the packages of the step. This is the only valid response.",
|
|
127
127
|
inputSchema: routeSchema,
|
|
128
|
+
view: "none",
|
|
128
129
|
describeCall: "none",
|
|
129
130
|
execute: () => Promise.resolve(ok({ recorded: true })),
|
|
130
131
|
});
|
|
@@ -29,6 +29,7 @@ export const alphafoldPredictionTool = defineTool({
|
|
|
29
29
|
inputSchema: z.object({
|
|
30
30
|
uniprotAccession: z.string().min(1).describe("A UniProt accession, for example 'P38398' (BRCA1) or 'P69905' (hemoglobin subunit alpha)."),
|
|
31
31
|
}),
|
|
32
|
+
view: "none",
|
|
32
33
|
describeCall: ({ uniprotAccession }) => uniprotAccession,
|
|
33
34
|
execute: async ({ uniprotAccession }) => {
|
|
34
35
|
// The client trims before it builds the URL, thus a miss echoes the same
|
package/dist/tools/bio/chembl.js
CHANGED
|
@@ -127,6 +127,7 @@ export const chemblTool = defineTool({
|
|
|
127
127
|
"compounds here first. If ChEMBL misses the compound, resolve it via pubchem action='compound' and bridge back with pubchem action='crossrefs'.\n" +
|
|
128
128
|
"An empty array is valid no-data, not an error — do not retry the same call.",
|
|
129
129
|
inputSchema,
|
|
130
|
+
view: "none",
|
|
130
131
|
describeCall: "none",
|
|
131
132
|
execute: async ({ action, query, searchType, chemblId, idType, activityType, limit }) => {
|
|
132
133
|
switch (action) {
|
|
@@ -112,6 +112,7 @@ export function createComptoxTool(deps) {
|
|
|
112
112
|
"This is environmental/industrial-chemical data: for drug-like compounds prefer PubChem or ChEMBL — dataset 'chemical' returns " +
|
|
113
113
|
"`detail.pubchemCid` to bridge back.",
|
|
114
114
|
inputSchema,
|
|
115
|
+
view: "none",
|
|
115
116
|
describeCall: "none",
|
|
116
117
|
execute: async (input) => {
|
|
117
118
|
const headers = getEpaCcteHeaders(deps.apiKey);
|
|
@@ -118,6 +118,7 @@ export function createDrugGeneInteractionsTool(deps) {
|
|
|
118
118
|
"not be reached (a missing DRUGBANK_API_KEY lands here — state that the key is missing, and proceed with DGIdb). A `found: false` entry means the identifier " +
|
|
119
119
|
"is not in DGIdb at all, usually a non-HUGO symbol or a brand name. Neither is worth retrying unchanged.",
|
|
120
120
|
inputSchema,
|
|
121
|
+
view: "none",
|
|
121
122
|
describeCall: "none",
|
|
122
123
|
execute: async (input) => {
|
|
123
124
|
const inputs = typeof input.query === "string" ? [input.query] : input.query;
|
|
@@ -170,6 +170,7 @@ export function createGeneDiseaseEvidenceTool(deps) {
|
|
|
170
170
|
"could not be reached (a missing DISGENET_API_KEY lands here — state that the key is missing, and proceed with the others); 'not_applicable' means that " +
|
|
171
171
|
"corpus has no lookup for this queryType. Only 'no_data' is evidence of absence, and none is worth retrying unchanged.",
|
|
172
172
|
inputSchema,
|
|
173
|
+
view: "none",
|
|
173
174
|
describeCall: "none",
|
|
174
175
|
execute: async (input) => {
|
|
175
176
|
const applicable = applicableSources(input.queryType);
|
|
@@ -138,6 +138,7 @@ export const genePreclinicalProfileTool = defineTool({
|
|
|
138
138
|
"with no phenotype terms means the gene has not been IMPC-phenotyped, which is common; an empty hpoTerms list means Monarch curates no human " +
|
|
139
139
|
"phenotype for the gene, and a null geneCurie means the symbol anchored on no HGNC id.",
|
|
140
140
|
inputSchema,
|
|
141
|
+
view: "none",
|
|
141
142
|
describeCall: "none",
|
|
142
143
|
execute: async ({ geneSymbol, include, species, minRank, tissueLimit, phenotypeLimit }) => {
|
|
143
144
|
const halves = include ?? ["expression", "knockout"];
|
|
@@ -156,6 +156,7 @@ export const lookupAnnotationTool = defineTool({
|
|
|
156
156
|
"An empty terms / annotations / pathways array is valid no-data — report it and continue, do not retry the same input. An empty pathways array " +
|
|
157
157
|
"for a pathwayId means neither database holds that identifier.",
|
|
158
158
|
inputSchema,
|
|
159
|
+
view: "none",
|
|
159
160
|
describeCall: "none",
|
|
160
161
|
execute: async ({ vocabulary, query, pathwayId, goId, geneProductId, taxonId, organism, includeGenes, limit, }) => {
|
|
161
162
|
const cap = limit ?? DEFAULT_LIMIT;
|
|
@@ -89,6 +89,7 @@ export const openTargetsTool = defineTool({
|
|
|
89
89
|
"NO-DATA IS FINAL — do not retry the same id. Empty associations mean no evidence, or an id the platform does not hold. An empty candidates " +
|
|
90
90
|
"list means the name matched no disease: rephrase it, do not invent an id.",
|
|
91
91
|
inputSchema,
|
|
92
|
+
view: "none",
|
|
92
93
|
describeCall: "none",
|
|
93
94
|
execute: async (input) => {
|
|
94
95
|
const limit = input.limit ?? DEFAULT_LIMIT;
|
|
@@ -83,6 +83,7 @@ export const pubchemTool = defineTool({
|
|
|
83
83
|
"then chembl action='mechanism' / 'bioactivity' there for curated activity data. " +
|
|
84
84
|
"An empty results / crossRefs / assays array is valid no-data (no match; the CID is in no external registry; the compound was never screened) — do not retry.",
|
|
85
85
|
inputSchema,
|
|
86
|
+
view: "none",
|
|
86
87
|
describeCall: "none",
|
|
87
88
|
execute: async (input) => {
|
|
88
89
|
switch (input.action) {
|
package/dist/tools/bio/pubmed.js
CHANGED
|
@@ -163,6 +163,7 @@ export function createPubMedTool(deps) {
|
|
|
163
163
|
"Report it and continue, do not retry it.",
|
|
164
164
|
inputSchema,
|
|
165
165
|
outputSchema,
|
|
166
|
+
view: "none",
|
|
166
167
|
// One tool, three jobs. The action alone would still render three
|
|
167
168
|
// different searches identically, so each action names its own subject:
|
|
168
169
|
// the query, how many articles, or which article.
|
|
@@ -76,6 +76,7 @@ export const searchClinicalTrialsTool = defineTool({
|
|
|
76
76
|
message: "nctId is required when action is 'details' — one NCT ID, e.g. 'NCT00000102'. Find one with action 'search'.",
|
|
77
77
|
path: ["nctId"],
|
|
78
78
|
}),
|
|
79
|
+
view: "none",
|
|
79
80
|
describeCall: "none",
|
|
80
81
|
execute: async ({ action, query, nctId, phase, status, limit, includeSummaries }) => {
|
|
81
82
|
if (action === "details") {
|
|
@@ -70,6 +70,7 @@ export const searchFaersTool = defineTool({
|
|
|
70
70
|
message: `limit is capped at ${LABEL_MAX_LIMIT} for action 'label' — each label version carries whole prose sections`,
|
|
71
71
|
path: ["limit"],
|
|
72
72
|
}),
|
|
73
|
+
view: "none",
|
|
73
74
|
describeCall: "none",
|
|
74
75
|
execute: async ({ action, drugName, limit, serious }) => {
|
|
75
76
|
switch (action) {
|
|
@@ -66,6 +66,7 @@ export const searchGeneTool = defineTool({
|
|
|
66
66
|
.describe("Gene identifiers to look up, mixed freely (e.g. ['BRCA1', 'ENSG00000141510', 'P38398', 'HGNC:1100', 'CHEMBL5619'])"),
|
|
67
67
|
species: z.string().default("homo_sapiens").describe("Species name (e.g. 'homo_sapiens', 'mus_musculus')"),
|
|
68
68
|
}),
|
|
69
|
+
view: "none",
|
|
69
70
|
// The identifiers are the call. A large batch is summarized here rather than
|
|
70
71
|
// left to the emit-site cap: that cap cuts at a character, so a 200-symbol
|
|
71
72
|
// join arrives ending in `TP5` — a fragment that reads as a real symbol and
|
|
@@ -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.
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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. */
|