@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.
Files changed (115) 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 +5 -3
  9. package/dist/index.js +2 -1
  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/runtime/dbos.d.ts +6 -0
  23. package/dist/runtime/dbos.js +14 -2
  24. package/dist/sandbox/sandbox-step-translate.d.ts +5 -9
  25. package/dist/sandbox/sandbox-step-translate.js +5 -9
  26. package/dist/state/artifacts.d.ts +4 -1
  27. package/dist/state/artifacts.js +4 -1
  28. package/dist/state/index.d.ts +2 -2
  29. package/dist/state/index.js +1 -1
  30. package/dist/state/plans.d.ts +27 -0
  31. package/dist/state/plans.js +57 -3
  32. package/dist/tasks/data-profile-activity.d.ts +3 -7
  33. package/dist/tasks/data-profile-activity.js +1 -1
  34. package/dist/tasks/data-profile.js +3 -6
  35. package/dist/tools/ad-hoc-router.js +1 -0
  36. package/dist/tools/bio/alphafold-prediction.js +1 -0
  37. package/dist/tools/bio/chembl.js +1 -0
  38. package/dist/tools/bio/comptox.js +1 -0
  39. package/dist/tools/bio/drug-gene-interactions.js +1 -0
  40. package/dist/tools/bio/gene-disease-evidence.js +1 -0
  41. package/dist/tools/bio/gene-preclinical-profile.js +1 -0
  42. package/dist/tools/bio/lookup-annotation.js +1 -0
  43. package/dist/tools/bio/opentargets.js +1 -0
  44. package/dist/tools/bio/pubchem.js +1 -0
  45. package/dist/tools/bio/pubmed.js +1 -0
  46. package/dist/tools/bio/search-clinical-trials.js +1 -0
  47. package/dist/tools/bio/search-faers.js +1 -0
  48. package/dist/tools/bio/search-gene.js +1 -0
  49. package/dist/tools/bio/search-geo-datasets.js +1 -0
  50. package/dist/tools/bio/search-interactions.js +1 -0
  51. package/dist/tools/bio/search-patents.js +1 -0
  52. package/dist/tools/bio/search-protein.js +1 -0
  53. package/dist/tools/bio/search-regulatory-approvals.js +1 -0
  54. package/dist/tools/bio/target-safety.js +1 -0
  55. package/dist/tools/capability-data.js +5 -0
  56. package/dist/tools/define-tool.d.ts +18 -0
  57. package/dist/tools/define-tool.js +2 -0
  58. package/dist/tools/display/show-user.js +1 -0
  59. package/dist/tools/execute-analysis.js +4 -0
  60. package/dist/tools/knowledge/check.d.ts +47 -1
  61. package/dist/tools/knowledge/check.js +10 -1
  62. package/dist/tools/knowledge/client.d.ts +1 -1
  63. package/dist/tools/knowledge/recommend.js +4 -3
  64. package/dist/tools/knowledge/situation.d.ts +39 -1
  65. package/dist/tools/knowledge/situation.js +80 -8
  66. package/dist/tools/knowledge/skeleton.d.ts +7 -0
  67. package/dist/tools/knowledge/skeleton.js +88 -2
  68. package/dist/tools/knowledge/template.js +1 -0
  69. package/dist/tools/memory/update-working-memory.js +5 -0
  70. package/dist/tools/read-tool-output.js +4 -0
  71. package/dist/tools/report-authoring/authoring-tools.js +8 -0
  72. package/dist/tools/report-session/examine-page.js +5 -0
  73. package/dist/tools/report-session/list-artifacts.js +5 -0
  74. package/dist/tools/report-session/preview-report.js +1 -0
  75. package/dist/tools/report-session/record-version.js +1 -0
  76. package/dist/tools/report-session/script-tools.js +2 -0
  77. package/dist/tools/research/context7-docs.js +2 -0
  78. package/dist/tools/research/generate-analogy-report.js +3 -0
  79. package/dist/tools/research/generate-plan.js +6 -0
  80. package/dist/tools/research/inspect-data-profile.js +5 -0
  81. package/dist/tools/research/inspect-run.js +5 -0
  82. package/dist/tools/research/resolve-citation.js +1 -0
  83. package/dist/tools/research/search-arxiv.js +1 -0
  84. package/dist/tools/research/search-github-repos.js +1 -0
  85. package/dist/tools/research/search-semantic-scholar.js +1 -0
  86. package/dist/tools/research/web-fetch.js +1 -0
  87. package/dist/tools/research/web-search.js +1 -0
  88. package/dist/tools/sandbox/lease-tools.js +2 -0
  89. package/dist/tools/sandbox/link-packages.js +1 -0
  90. package/dist/tools/sandbox/list-available-packages.js +5 -0
  91. package/dist/tools/sandbox/list-available-refs.js +5 -0
  92. package/dist/tools/sandbox/report-blocker.js +1 -0
  93. package/dist/tools/sandbox/skills.js +2 -0
  94. package/dist/tools/sandbox/submit-file-metadata.js +1 -0
  95. package/dist/tools/script/file-tools.js +6 -0
  96. package/dist/tools/script/run-script.js +20 -1
  97. package/dist/tools/start-report-session.js +1 -0
  98. package/dist/tools/tool-view.d.ts +63 -0
  99. package/dist/tools/tool-view.js +68 -0
  100. package/dist/tools/workspace/edit-file.js +5 -0
  101. package/dist/tools/workspace/execute-command.js +5 -2
  102. package/dist/tools/workspace/file-stat.js +5 -0
  103. package/dist/tools/workspace/grep.js +4 -0
  104. package/dist/tools/workspace/list-files.js +5 -0
  105. package/dist/tools/workspace/read-file.js +4 -0
  106. package/dist/tools/workspace/scan-inputs.js +5 -0
  107. package/dist/tools/workspace/show-file.js +1 -0
  108. package/dist/tools/workspace/show-plan.js +1 -0
  109. package/dist/tools/workspace/workspace-search.js +5 -0
  110. package/dist/tools/workspace/write-file.js +7 -2
  111. package/dist/workflows/execute-analysis.js +2 -1
  112. package/dist/workflows/sandbox-step.js +3 -7
  113. package/package.json +1 -1
  114. package/dist/tools/detail-resolver.d.ts +0 -40
  115. 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
- * `resolveDetail` is built over the profiler agent's own tool list, so the
68
- * phrase comes from the called tool's `describeCall` hook. It is passed per
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, input: unknown, resolveDetail?: ToolDetailResolver): Promise<void>;
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, input, resolveDetail) => emit("executing", activityForTool(name, input, resolveDetail), toolUseId),
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, withRunScript } from "../loop/run-loop.js";
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.input, resolveDetail);
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
@@ -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) {
@@ -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.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. */