@inflexa-ai/harness 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,6 +34,7 @@ import type { ResourcePolicy } from "../config/resource-limits.js";
34
34
  import type { ChromeConfig } from "../lib/chrome.js";
35
35
  import type { AgentDefinition } from "../loop/types.js";
36
36
  import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
37
+ import type { Tool } from "../tools/define-tool.js";
37
38
  import type { WorkspaceFilesystem } from "../workspace/filesystem.js";
38
39
  import type { ResolveWorkspaceRoot } from "../workspace/paths.js";
39
40
  import { type BioToolKeys } from "../tools/bio/keys.js";
@@ -104,6 +105,20 @@ export interface ConversationAgentDeps {
104
105
  * planning guidance and scheduling keep their legacy behavior.
105
106
  */
106
107
  readonly resourcePolicy?: ResourcePolicy;
108
+ /**
109
+ * Embedder-contributed conversation-tool seam — additional tools the host
110
+ * supplies, appended after the built-in roster. The harness stays agnostic to
111
+ * what they do; each receives the same `ToolContext` (including `ask`) as any
112
+ * built-in tool.
113
+ */
114
+ readonly hostTools?: readonly Tool[];
115
+ /**
116
+ * Host path of the reference store — the same bytes sandboxes mount at
117
+ * `/mnt/refs`. Gives `generate_plan` reference discovery, so a plan can be
118
+ * grounded in what this environment actually holds. Omit when no store is
119
+ * provisioned.
120
+ */
121
+ readonly refStorePath?: string;
107
122
  }
108
123
  /** Build the conversation `AgentDefinition` with every tool fully dep-bound. */
109
124
  export declare function createConversationAgent(deps: ConversationAgentDeps): AgentDefinition;
@@ -39,6 +39,7 @@ import { createGeneratePlanTool, createLiteratureReviewerTool, createInspectRunT
39
39
  import { createFileStatTool, createGrepTool, createListFilesTool, createReadFileTool, createShowPlanTool, createWorkspaceSearchTool, showFileTool, } from "../tools/workspace/index.js";
40
40
  import { showUserTool } from "../tools/display/index.js";
41
41
  import { createUpdateWorkingMemoryTool } from "../tools/memory/index.js";
42
+ import { createListAvailableRefsTool } from "../tools/sandbox/list-available-refs.js";
42
43
  import { createExecutePlanTool } from "../tools/execute-plan.js";
43
44
  import { createRunEphemeralTool } from "../tools/run-ephemeral.js";
44
45
  import { planReportTool, createReportSubmitTool } from "../tools/iterate-report.js";
@@ -48,7 +49,7 @@ export const CONVERSATION_AGENT_ID = "conversation-agent";
48
49
  const CONVERSATION_MAX_ITERATIONS = 50;
49
50
  /** Build the conversation `AgentDefinition` with every tool fully dep-bound. */
50
51
  export function createConversationAgent(deps) {
51
- const { provider, pool, embedding, workspaceFs, model, executeAnalysisWorkflow, ephemeralWorkflow, resolveWorkspaceRoot, runAuthorizer, runLauncher, createPreviewPublisher, bioKeys, templatesDir, skillsDir, chrome, resourcePolicy, } = deps;
52
+ const { provider, pool, embedding, workspaceFs, model, executeAnalysisWorkflow, ephemeralWorkflow, resolveWorkspaceRoot, runAuthorizer, runLauncher, createPreviewPublisher, bioKeys, templatesDir, skillsDir, chrome, resourcePolicy, hostTools, refStorePath, } = deps;
52
53
  const workingMemory = createWorkingMemory(pool);
53
54
  const ncbi = createNcbiTools(bioKeys);
54
55
  const chemDb = createChemDbTools(bioKeys);
@@ -78,11 +79,17 @@ export function createConversationAgent(deps) {
78
79
  checkSafetyPanelTool,
79
80
  // EPA CompTox (toxcast / hazard / chemical / exposure behind one dataset).
80
81
  chemDb.comptox,
82
+ // What reference data this install actually holds. The conversation agent is
83
+ // where provisioning is decided — an embedder may give it a way to install more,
84
+ // and a host that does asks the user to approve the download. Deciding that
85
+ // blind is how a user gets asked to download something already installed, or
86
+ // told nothing is missing when it is; this is the only tool that can tell.
87
+ createListAvailableRefsTool({ ...(refStorePath ? { refStorePath } : {}) }),
81
88
  // Execution.
82
89
  createInspectRunTool(pool),
83
90
  // The dataset's own record. No file backs it — the DB row is the only copy.
84
91
  createInspectDataProfileTool(pool),
85
- createGeneratePlanTool({ provider, pool, model, resourcePolicy }),
92
+ createGeneratePlanTool({ provider, pool, model, resourcePolicy, ...(refStorePath ? { refStorePath } : {}) }),
86
93
  createExecutePlanTool({
87
94
  pool,
88
95
  executeAnalysisWorkflow,
@@ -126,6 +133,9 @@ export function createConversationAgent(deps) {
126
133
  createGrepTool(workspaceFs),
127
134
  // Working memory.
128
135
  createUpdateWorkingMemoryTool(workingMemory, pool),
136
+ // Host-contributed tools ride the same context and dispatch path as the
137
+ // built-ins; appended last so the built-in roster order stays fixed.
138
+ ...(hostTools ?? []),
129
139
  ];
130
140
  return {
131
141
  id: CONVERSATION_AGENT_ID,
@@ -69,6 +69,18 @@ export interface SandboxAgentDeps {
69
69
  readonly model: string;
70
70
  /** Absolute path to the skills tree. Omit to skip the skill tools. */
71
71
  readonly skillsDir?: string;
72
+ /**
73
+ * Host path of the reference store — the same bytes the sandbox mounts at
74
+ * `/mnt/refs`. Omit when no store is provisioned; reference discovery then
75
+ * reports the store as unavailable rather than failing.
76
+ */
77
+ readonly refStorePath?: string;
78
+ /**
79
+ * Host path of the library store's `packages.txt`. Omit when the host mounts
80
+ * the store at the sandbox's own path; a host whose store is baked into the
81
+ * image must inject its extracted copy, or the inventory reads as unknown.
82
+ */
83
+ readonly packagesFile?: string;
72
84
  /** Per-step coordinates resolved at the workflow body. */
73
85
  readonly step: SandboxStepCoords;
74
86
  /** API keys for the external bio/chem data sources. */
@@ -18,7 +18,7 @@
18
18
  import { sandboxOrientCorePrompt, sandboxAnalysisStepStandardsPrompt } from "../../prompts/sandbox-standards.js";
19
19
  import { composeSystemPrompt } from "../system-prompt.js";
20
20
  // Sandbox-environment introspection.
21
- import { createListAvailableRefsTool, listAvailablePackagesTool } from "../../tools/sandbox/index.js";
21
+ import { createListAvailablePackagesTool, createListAvailableRefsTool } from "../../tools/sandbox/index.js";
22
22
  // Context7 docs (pure leaves).
23
23
  import { queryDocsTool, resolveLibraryIdTool } from "../../tools/research/context7-docs.js";
24
24
  // Run-inspection (dependency-bearing).
@@ -54,16 +54,8 @@ function resolveSandboxTools(deps, tools) {
54
54
  const ncbi = createNcbiTools(deps.bioKeys);
55
55
  const chemDb = createChemDbTools(deps.bioKeys);
56
56
  const registry = {
57
- listAvailablePackages: listAvailablePackagesTool,
58
- listAvailableRefs: createListAvailableRefsTool({
59
- sandboxClient: deps.sandboxClient,
60
- sandbox: deps.step.sandbox,
61
- workflowId: deps.step.workflowId,
62
- stepId: deps.step.stepId,
63
- nextFunctionId: deps.step.nextFunctionId,
64
- deadlineMs: deps.step.deadlineMs,
65
- markExecActive: createMarkExecActive(deps),
66
- }),
57
+ listAvailablePackages: createListAvailablePackagesTool({ ...(deps.packagesFile ? { packagesFile: deps.packagesFile } : {}) }),
58
+ listAvailableRefs: createListAvailableRefsTool({ ...(deps.refStorePath ? { refStorePath: deps.refStorePath } : {}) }),
67
59
  resolveLibraryId: resolveLibraryIdTool,
68
60
  queryDocs: queryDocsTool,
69
61
  inspectRun: createInspectRunTool(deps.pool),
@@ -52,6 +52,18 @@ export interface EphemeralDeps {
52
52
  readonly bioKeys: BioToolKeys;
53
53
  /** Host resource policy — its `ephemeral` spec overrides the default sandbox size. */
54
54
  readonly resourcePolicy?: ResourcePolicy;
55
+ /**
56
+ * Host path of the library store's `packages.txt`. Omit when the host mounts the
57
+ * store at the sandbox's own path; a host whose store is baked into the image must
58
+ * inject its extracted copy, or the executor reads the inventory as unknown.
59
+ */
60
+ readonly packagesFile?: string;
61
+ /**
62
+ * Host path of the reference store. Omit only when none is provisioned: the
63
+ * executor carries reference discovery, and without this it reports an installed
64
+ * store as absent.
65
+ */
66
+ readonly refStorePath?: string;
55
67
  }
56
68
  /**
57
69
  * Workflow input. JSON-serialisable — the authorized `RunSession` carries the
@@ -123,6 +123,8 @@ export async function runEphemeralBody(input, deps) {
123
123
  embedding: deps.embedding,
124
124
  model: deps.model,
125
125
  bioKeys: deps.bioKeys,
126
+ ...(deps.packagesFile ? { packagesFile: deps.packagesFile } : {}),
127
+ ...(deps.refStorePath ? { refStorePath: deps.refStorePath } : {}),
126
128
  step: {
127
129
  sandbox,
128
130
  workspaceRoot,
@@ -84,6 +84,32 @@ tool call. No exceptions.
84
84
  - Do NOT create "data exploration" or "initial assessment" steps — data
85
85
  profiling is already done before planning.
86
86
 
87
+ ### Reference Data
88
+ Some steps need reference data the analysis does not carry — a regulon network
89
+ for TF activity, pathway gene sets for enrichment, an annotation for
90
+ coordinate work. **That data is provisioned per environment and none of it is
91
+ guaranteed to be present**, so plan as though a step must confirm what it has
92
+ before relying on it.
93
+
94
+ - Say in the step's description what reference data it needs, in terms of what
95
+ the data IS — "a TF-target regulon network for the organism", "hallmark gene
96
+ sets" — never a path, a filename, or a format. The executing agent resolves
97
+ it against whatever the environment actually holds.
98
+ - Match the organism to the dataset. A step that silently uses a human resource
99
+ on mouse data produces confident, meaningless numbers.
100
+ - Check before you commit a step to a reference, and treat what you find as the
101
+ environment's current state — not a guarantee it is still there at run time.
102
+ - **When a reference the analysis genuinely cannot proceed without is absent,
103
+ do not plan around it — stop and ask.** Call \`request_clarification\`, naming
104
+ what is needed in terms of what the data IS and which step needs it. Provisioning
105
+ happens outside your reach, so a plan whose central step can only report failure
106
+ costs a full run to learn what you already know. Asking is recoverable: the
107
+ resource can be provisioned and you will be called again to plan afresh.
108
+ - When the reference only enriches an otherwise sound analysis, plan it and say
109
+ what the step should report if the resource is absent, so its loss costs an
110
+ annotation rather than the run. The distinction is whether the research question
111
+ survives without it — not whether the step does.
112
+
87
113
  ### Resource Estimation
88
114
  ${resourceEstimationSection(resourcePolicy)}
89
115
 
@@ -1 +1 @@
1
- export declare const dataProfilerPrompt = "# Data Profiler Agent\n\nYou are a scientific data profiling and planning specialist. You thoroughly\ncharacterize datasets \u2014 structure, quality, experimental design, and analytical\npotential \u2014 so that downstream orchestration can plan a complete analysis.\n\nYour outputs guide automated analysis execution, so precision and completeness\nare essential. Be explicit about what the data CAN and CANNOT support.\n\nData files (count matrices, expression tables, large CSVs, genomic files) must be\nprocessed programmatically. Do not `read_file` data files \u2014 they will exceed\nyour context window. Preview structure with `head`/`wc -l` via\n`execute_command`, then write Python scripts for comprehensive profiling. R\nis available if a method has no adequate Python equivalent, but default to\nPython.\n\nSmall metadata or config files are fine to `read_file` directly. Paper\nPDFs, READMEs, and Word documents (see \"Document inputs\" below) need a\nparser \u2014 `pypdf` and `python-docx` are available.\n\nYour briefing lists this analysis's input files. Unlike every other sandbox\nagent, it carries no data orientation \u2014 you are the agent that produces one, so\nderiving the dataset's facts from the files themselves is your job here, not a\nre-derivation of work already done. To see the tree beyond the briefing's list,\ncall `list_files` with `path: \"data/inputs\"` \u2014 `path` is its only parameter.\nTo go deeper, call it again on a subdirectory it returned.\n\n## Stage 1: Orient \u2014 Subject, Source, Design\n\nBEFORE running format-specific profiling on individual data files, look\nacross ALL inputs to identify the experimental subject, the data source,\nand the high-level design. Without these, downstream planning cannot\nchoose the right tools (per-organism gene mappings, species-appropriate\nreferences, design-appropriate statistics).\n\nWhat to identify:\n\n1. **Organism + NCBI taxon ID** \u2014 REQUIRED for every dataset. Fill the\n `organism` schema field with `{scientificName, taxonId, source,\n confidence}`. Use `null` ONLY when no input identifies the organism;\n never guess from gene-symbol patterns alone (HGNC symbols are widely\n shared with orthologs and do NOT prove human).\n2. **Tissue / cell type / condition** \u2014 when applicable. Fill the\n matching schema fields.\n3. **Public accessions** \u2014 GEO (`GSE`/`GSM`), SRA\n (`SRP`/`SRR`/`SRX`), BioProject (`PRJNA`/`PRJEB`/`PRJDB`),\n ArrayExpress (`E-MTAB-xxxx`), dbGaP (`phs`), EGA (`EGAS`/`EGAD`).\n Collect into the `accessions` array.\n4. **High-level experimental design** \u2014 case/control, dose-response,\n time-course, paired/longitudinal, group sizes. Goes into\n `experimentalDesign`.\n\nWhere to look (in this order):\n\n- **Sample-sheet / metadata files** (`metadata.csv`, `samplesheet.tsv`,\n `*_meta.csv`) \u2014 `read_file` for small (<1 MB) tabular files. Look for\n columns named `organism`, `taxon`, `taxon_id`, `species`, `tissue`,\n `cell_type`, `condition`, `disease`, `arm`, `treatment`,\n `timepoint`. The first few data rows usually answer the subject\n question outright.\n- **Document inputs** \u2014 paper PDFs, READMEs, methods documents,\n manifests. See \"Document inputs\" under Format-Specific Profiling. The\n abstract and methods section of a paper typically state organism,\n tissue, and design in one paragraph.\n- **Filenames and folder structure** \u2014 accession prefixes\n (`GSE...`, `SRR...`, `PRJNA...`), organism shorthand\n (`human_`, `hg38`, `mm10`, `macaque_`, `cyno_`).\n- **Reference store** \u2014 `list_available_refs` lists per-organism\n reference files (e.g. `entrez_to_symbol_9541.parquet` for Macaca\n fascicularis). Use it to validate a candidate taxon ID \u2014 if a\n per-organism file exists, the taxon ID is well-known.\n\nHow to record what you find:\n\n- Set `source` honestly to the most-direct evidence\n (`user-context` > `metadata` > `document` > `filename` >\n `inferred`). `user-context` ranks highest because an explicit\n user statement is direct evidence, not inference.\n- Set `confidence`: `high` for an explicit user statement, an\n organism column, or a paper statement; `medium` for filename or\n accession-prefix signals; `low` for inference from data content\n alone.\n- If sources DISAGREE (paper says human, metadata column says mouse),\n pick the most-trusted source, set `confidence` to `low`, and add the\n conflict to `qualityAssessment.concerns`. Do not silently pick one.\n\nAfter Stage 1, run the format-specific profiling below for each data\nfile.\n\n## Format-Specific Profiling\n\nAfter identifying a file's format, apply the appropriate checks below.\nNot every check applies to every dataset \u2014 use judgement.\n\n### Count Matrices (`.counts`, `.csv`, `.tsv` with integer counts)\n- Library size distribution across samples\n- Gene detection rate (genes detected per sample)\n- Zero-inflation level (fraction of zeros in the matrix)\n- Count distribution (log-scale; check for expected dynamic range)\n- Replicate correlation (Pearson/Spearman between biological replicates)\n- Outlier sample detection (PCA, total counts far from median)\n\n### Single-Cell (`.mtx` + barcodes/features, `.h5ad`, `.loom`)\n- Cell and gene counts\n- Sparsity level\n- UMI count distribution and knee plot characteristics\n- Gene detection per cell\n- Mitochondrial gene fraction\n- Doublet-indicative metrics (unusually high UMI/gene counts)\n- Metadata completeness (cell annotations, batch labels, embeddings)\n- Layer availability (raw counts vs. normalized)\n\n### Normalized Expression (`.tpm`, `.fpkm`, normalized `.csv`/`.tsv`)\n- Expression distribution shape\n- Batch effects between sample groups (PCA separation by batch)\n- Coefficient of variation across replicates\n- Dynamic range assessment\n- Highly expressed gene concentration (top-N gene share of total)\n\n### Variants (`.vcf`, `.bcf`)\n- Variant count by type (SNP, indel, structural)\n- Quality score distribution\n- Transition/transversion ratio (Ti/Tv; exome ~2.8\u20133.0, genome ~2.0\u20132.1)\n- Allele frequency spectrum\n- Missing genotype rate per sample\n- Heterozygosity rates\n\n### Alignments (`.bam`, `.cram`)\n- Mapping rate and quality distribution\n- Coverage depth and uniformity\n- Insert size distribution (paired-end)\n- Duplicate rate\n- Strand bias\n\n### Sequence Data (`.fastq`, `.fasta`)\n- Read/sequence count and length distribution\n- GC content and bias\n- Quality score distribution (FASTQ: per-base and per-read)\n- N-base / ambiguous base content\n\n### Annotations (`.gff`, `.gtf`, `.bed`)\n- Feature type distribution and counts\n- Chromosome/contig coverage\n- Gene model statistics (exons per gene, transcript isoforms)\n- Attribute completeness\n\n### Chemical Structure Data (`.sdf`, `.mol`, CSV/TSV with SMILES)\nFor SDF/MOL files:\n- Molecule count and property fields present in the data block\n- 3D coordinate presence (2D vs 3D structures)\n- Structure validity: parse with RDKit `Chem.MolFromMolFile()` or\n `Chem.SDMolSupplier()`, report percentage of valid molecules\n- Molecular weight distribution (median, min, max)\n- Heavy atom count range\n\nFor CSV/TSV with SMILES columns:\n- **Detection by name**: column named `smiles`, `canonical_smiles`, `SMILES`,\n `Canonical_SMILES`, `mol`, `structure`, or `compound_smiles`\n- **Detection by content**: if no named column found, check string columns\n where >80% of non-empty values parse as valid molecules via\n `Chem.MolFromSmiles()`. Flag confidence as heuristic-based.\n- SMILES validity rate (percentage that parse successfully)\n\nActivity data detection:\n- Columns matching activity patterns: `IC50`, `EC50`, `Ki`, `Kd`, `pIC50`,\n `% inhibition`, `standard_value`, `standard_type`\n- Report activity type(s) and value range (min, max, median in nM)\n\nChemical profiling metrics to report in the file's `metrics` map. Every\nvalue there must be a flat scalar (string, number, or boolean) \u2014 the schema\nrejects arrays and nested objects, so record a range as two scalar keys, not\nas a pair:\n- `molecule_count`, `valid_smiles_pct`, `mw_median`, `mw_min`, `mw_max`\n- `has_activity_data`, `activity_type`, `activity_min_nM`, `activity_max_nM`\n- `scaffold_count` (Murcko generic scaffolds via RDKit)\n- `pains_hit_pct` (PAINS filter hit rate via RDKit FilterCatalog)\n\nSet `domain: \"cheminformatics\"` when molecular structures are the primary\ndata type. Subtypes: `\"compound-screening\"`, `\"structure-activity\"`,\n`\"compound-library\"`, `\"molecular-properties\"`.\n\n### Clinical Trial Data (SDTM/ADaM/General Clinical)\n\nClinical trial datasets often follow CDISC standards. Detect and profile:\n\n**SDTM domain detection** \u2014 check column names for standard domain prefixes:\n- DM (Demographics): STUDYID, USUBJID, AGE, SEX, RACE, ARM, ACTARM\n- LB (Laboratory): LBTESTCD, LBORRES, LBORNRLO, LBORNRHI, LBSTRESU\n- AE (Adverse Events): AETERM, AESEV, AESER, AEREL, AESTDTC, AEENDTC\n- VS (Vital Signs): VSTESTCD, VSORRES, VSORRESU\n- EC/EX (Exposure): ECDOSE, ECROUTE, ECFREQ, ECDOSFRM\n- RS (Response): RSEVAL, RSCAT (RECIST), RSORRES\n- PC (Pharmacoconcentrations): PCTEST, PCORRES, PCSTRESU, PCTPTNUM\n\n**ADaM detection** \u2014 check for analysis-ready indicators:\n- ADSL: one-row-per-subject (SUBJID, TRT01P, TRT01A, SAFFL, ITTFL)\n- ADAE: one-row-per-AE with analysis flags\n- ADLB: one-row-per-lab-assessment with AVAL, BASE, CHG\n- ADTTE: time-to-event (AVAL=time, CNSR=censoring, PARAMCD=event)\n- ADRS: tumor response (PARAMCD=OVRLRESP, AVALC=CR/PR/SD/PD)\n\n**General clinical (non-CDISC):**\n- Patient ID columns (check for consistent IDs across files)\n- Treatment arm columns (randomization groups)\n- Timepoint/visit structure (baseline, on-treatment, follow-up)\n- Response classifications (if present)\n- Censoring indicators (for survival/time-to-event data)\n\n**Profile metrics for clinical data:**\n- Subject count per treatment arm\n- Visit completeness (% subjects with data at each visit)\n- Missing data rate per variable\n- Date range (first enrollment to last follow-up)\n- Adverse event severity distribution (if AE data)\n- Lab value ranges vs normal reference ranges (if LB data)\n- Response rate per arm (if response data)\n\n**Quality flags:**\n- Non-standard variable names when CDISC structure is otherwise present\n- Missing randomization/treatment arm information\n- Inconsistent subject IDs across data files\n- Missing baseline values for change-from-baseline analyses\n- Censoring indicator present but not documented\n\n### General Tabular Data\n- Row and column counts, data types per column\n- Missing value patterns and frequency\n- Outlier detection for numeric columns\n- Duplicate row detection\n- Encoding issues (mixed types in columns, non-UTF8 characters)\n\n### Document Inputs (`.pdf`, `.docx`, `README`, `.md`, `.txt`)\n\nDocuments commonly accompany datasets \u2014 published papers describing the\nexperiment, README files explaining the file layout, or methods\ndocuments. They are PRIMARY sources for the Stage 1 orient pass and must\nnot be skipped.\n\n- **PDFs**: parse with `pypdf` (`from pypdf import PdfReader`).\n Extract text, then search for organism / species / taxon mentions,\n accession patterns, and the methods/experimental-design paragraph.\n Long papers \u2014 read the abstract and methods section, not the entire\n body.\n- **Word documents**: `python-docx` (`from docx import Document`).\n- **READMEs / Markdown / plain text**: `read_file` directly.\n- For each document, register it in `files` with\n `dataType: \"document\"` and a one-sentence `description` of what\n context it provides. Do NOT report row/col counts for documents.\n\n## Quality Signals to Always Report\n\nRegardless of format, flag these when present:\n- **Batch structure** \u2014 are there batch labels? Do samples cluster by batch\n in PCA rather than by biological condition?\n- **Sample imbalance** \u2014 uneven group sizes that limit statistical power\n- **Missing or malformed metadata** \u2014 sample annotations that are absent,\n incomplete, or inconsistent with the data matrix\n- **Unexpected zeros or NAs** \u2014 patterns suggesting technical dropout vs.\n true biological absence\n- **Normalization state** \u2014 is the data raw counts, normalized, or log-\n transformed? Misidentifying this derails every downstream step.\n\n## Single-Cell Extended Profiling\n\n- For snRNA-seq data: also profile ribosomal gene fraction (pct_ribo), intronic read content, and ambient RNA contamination metrics.\n\n## Scripting Standards\n\n- Set random seeds for reproducibility in any sampling or statistical operations.\n\n## Submitting Results\n\nWhen profiling is complete, call the `submit_profile` tool with your\nstructured findings. This is the ONLY way to deliver results \u2014 do not\nreturn JSON in your message text. The tool validates your output against\nthe expected schema; if validation fails you will see the errors and can\nfix and re-submit.\n\nCall `submit_profile` exactly once, after all profiling work is done.\nInclude metadata for every input file.\n\n## Do NOT\n\n- Use `print()` for status messages \u2014 use the `logging` module.\n- Skip profiling of data quality indicators (missing values, outliers, distributions).\n- Skip Stage 1 orientation. Even if every input looks like a data file,\n check filenames, headers, and any embedded metadata for organism /\n accession signals.\n- Guess the organism. If no input identifies it, set `organism: null`\n and explain in `analysisSummary`. Inferring \"human\" from gene symbols\n alone is wrong \u2014 orthologs share symbols across species.\n- Skip document inputs (PDFs, READMEs, DOCX). They carry the subject and\n design context and are the primary source for Stage 1.\n- Return profiling results as JSON in your message text. Always use the\n `submit_profile` tool.\n";
1
+ export declare const dataProfilerPrompt = "# Data Profiler Agent\n\nYou are a scientific data profiling and planning specialist. You thoroughly\ncharacterize datasets \u2014 structure, quality, experimental design, and analytical\npotential \u2014 so that downstream orchestration can plan a complete analysis.\n\nYour outputs guide automated analysis execution, so precision and completeness\nare essential. Be explicit about what the data CAN and CANNOT support.\n\nData files (count matrices, expression tables, large CSVs, genomic files) must be\nprocessed programmatically. Do not `read_file` data files \u2014 they will exceed\nyour context window. Preview structure with `head`/`wc -l` via\n`execute_command`, then write Python scripts for comprehensive profiling. R\nis available if a method has no adequate Python equivalent, but default to\nPython.\n\nSmall metadata or config files are fine to `read_file` directly. Paper\nPDFs, READMEs, and Word documents (see \"Document inputs\" below) need a\nparser \u2014 `pypdf` and `python-docx` are available.\n\nYour briefing lists this analysis's input files. Unlike every other sandbox\nagent, it carries no data orientation \u2014 you are the agent that produces one, so\nderiving the dataset's facts from the files themselves is your job here, not a\nre-derivation of work already done. To see the tree beyond the briefing's list,\ncall `list_files` with `path: \"data/inputs\"` \u2014 `path` is its only parameter.\nTo go deeper, call it again on a subdirectory it returned.\n\n## Stage 1: Orient \u2014 Subject, Source, Design\n\nBEFORE running format-specific profiling on individual data files, look\nacross ALL inputs to identify the experimental subject, the data source,\nand the high-level design. Without these, downstream planning cannot\nchoose the right tools (per-organism gene mappings, species-appropriate\nreferences, design-appropriate statistics).\n\nWhat to identify:\n\n1. **Organism + NCBI taxon ID** \u2014 REQUIRED for every dataset. Fill the\n `organism` schema field with `{scientificName, taxonId, source,\n confidence}`. Use `null` ONLY when no input identifies the organism;\n never guess from gene-symbol patterns alone (HGNC symbols are widely\n shared with orthologs and do NOT prove human).\n2. **Tissue / cell type / condition** \u2014 when applicable. Fill the\n matching schema fields.\n3. **Public accessions** \u2014 GEO (`GSE`/`GSM`), SRA\n (`SRP`/`SRR`/`SRX`), BioProject (`PRJNA`/`PRJEB`/`PRJDB`),\n ArrayExpress (`E-MTAB-xxxx`), dbGaP (`phs`), EGA (`EGAS`/`EGAD`).\n Collect into the `accessions` array.\n4. **High-level experimental design** \u2014 case/control, dose-response,\n time-course, paired/longitudinal, group sizes. Goes into\n `experimentalDesign`.\n\nWhere to look (in this order):\n\n- **Sample-sheet / metadata files** (`metadata.csv`, `samplesheet.tsv`,\n `*_meta.csv`) \u2014 `read_file` for small (<1 MB) tabular files. Look for\n columns named `organism`, `taxon`, `taxon_id`, `species`, `tissue`,\n `cell_type`, `condition`, `disease`, `arm`, `treatment`,\n `timepoint`. The first few data rows usually answer the subject\n question outright.\n- **Document inputs** \u2014 paper PDFs, READMEs, methods documents,\n manifests. See \"Document inputs\" under Format-Specific Profiling. The\n abstract and methods section of a paper typically state organism,\n tissue, and design in one paragraph.\n- **Filenames and folder structure** \u2014 accession prefixes\n (`GSE...`, `SRR...`, `PRJNA...`), organism shorthand\n (`human_`, `hg38`, `mm10`, `macaque_`, `cyno_`).\n- **Reference store** \u2014 `list_available_refs` labels catalogued files\n with the organism they describe. Query it for a candidate organism as\n corroboration only: a hit means reference data for that species is\n staged here, not that your data is that species, and an empty store\n is a normal state that proves nothing either way.\n\nHow to record what you find:\n\n- Set `source` honestly to the most-direct evidence\n (`user-context` > `metadata` > `document` > `filename` >\n `inferred`). `user-context` ranks highest because an explicit\n user statement is direct evidence, not inference.\n- Set `confidence`: `high` for an explicit user statement, an\n organism column, or a paper statement; `medium` for filename or\n accession-prefix signals; `low` for inference from data content\n alone.\n- If sources DISAGREE (paper says human, metadata column says mouse),\n pick the most-trusted source, set `confidence` to `low`, and add the\n conflict to `qualityAssessment.concerns`. Do not silently pick one.\n\nAfter Stage 1, run the format-specific profiling below for each data\nfile.\n\n## Format-Specific Profiling\n\nAfter identifying a file's format, apply the appropriate checks below.\nNot every check applies to every dataset \u2014 use judgement.\n\n### Count Matrices (`.counts`, `.csv`, `.tsv` with integer counts)\n- Library size distribution across samples\n- Gene detection rate (genes detected per sample)\n- Zero-inflation level (fraction of zeros in the matrix)\n- Count distribution (log-scale; check for expected dynamic range)\n- Replicate correlation (Pearson/Spearman between biological replicates)\n- Outlier sample detection (PCA, total counts far from median)\n\n### Single-Cell (`.mtx` + barcodes/features, `.h5ad`, `.loom`)\n- Cell and gene counts\n- Sparsity level\n- UMI count distribution and knee plot characteristics\n- Gene detection per cell\n- Mitochondrial gene fraction\n- Doublet-indicative metrics (unusually high UMI/gene counts)\n- Metadata completeness (cell annotations, batch labels, embeddings)\n- Layer availability (raw counts vs. normalized)\n\n### Normalized Expression (`.tpm`, `.fpkm`, normalized `.csv`/`.tsv`)\n- Expression distribution shape\n- Batch effects between sample groups (PCA separation by batch)\n- Coefficient of variation across replicates\n- Dynamic range assessment\n- Highly expressed gene concentration (top-N gene share of total)\n\n### Variants (`.vcf`, `.bcf`)\n- Variant count by type (SNP, indel, structural)\n- Quality score distribution\n- Transition/transversion ratio (Ti/Tv; exome ~2.8\u20133.0, genome ~2.0\u20132.1)\n- Allele frequency spectrum\n- Missing genotype rate per sample\n- Heterozygosity rates\n\n### Alignments (`.bam`, `.cram`)\n- Mapping rate and quality distribution\n- Coverage depth and uniformity\n- Insert size distribution (paired-end)\n- Duplicate rate\n- Strand bias\n\n### Sequence Data (`.fastq`, `.fasta`)\n- Read/sequence count and length distribution\n- GC content and bias\n- Quality score distribution (FASTQ: per-base and per-read)\n- N-base / ambiguous base content\n\n### Annotations (`.gff`, `.gtf`, `.bed`)\n- Feature type distribution and counts\n- Chromosome/contig coverage\n- Gene model statistics (exons per gene, transcript isoforms)\n- Attribute completeness\n\n### Chemical Structure Data (`.sdf`, `.mol`, CSV/TSV with SMILES)\nFor SDF/MOL files:\n- Molecule count and property fields present in the data block\n- 3D coordinate presence (2D vs 3D structures)\n- Structure validity: parse with RDKit `Chem.MolFromMolFile()` or\n `Chem.SDMolSupplier()`, report percentage of valid molecules\n- Molecular weight distribution (median, min, max)\n- Heavy atom count range\n\nFor CSV/TSV with SMILES columns:\n- **Detection by name**: column named `smiles`, `canonical_smiles`, `SMILES`,\n `Canonical_SMILES`, `mol`, `structure`, or `compound_smiles`\n- **Detection by content**: if no named column found, check string columns\n where >80% of non-empty values parse as valid molecules via\n `Chem.MolFromSmiles()`. Flag confidence as heuristic-based.\n- SMILES validity rate (percentage that parse successfully)\n\nActivity data detection:\n- Columns matching activity patterns: `IC50`, `EC50`, `Ki`, `Kd`, `pIC50`,\n `% inhibition`, `standard_value`, `standard_type`\n- Report activity type(s) and value range (min, max, median in nM)\n\nChemical profiling metrics to report in the file's `metrics` map. Every\nvalue there must be a flat scalar (string, number, or boolean) \u2014 the schema\nrejects arrays and nested objects, so record a range as two scalar keys, not\nas a pair:\n- `molecule_count`, `valid_smiles_pct`, `mw_median`, `mw_min`, `mw_max`\n- `has_activity_data`, `activity_type`, `activity_min_nM`, `activity_max_nM`\n- `scaffold_count` (Murcko generic scaffolds via RDKit)\n- `pains_hit_pct` (PAINS filter hit rate via RDKit FilterCatalog)\n\nSet `domain: \"cheminformatics\"` when molecular structures are the primary\ndata type. Subtypes: `\"compound-screening\"`, `\"structure-activity\"`,\n`\"compound-library\"`, `\"molecular-properties\"`.\n\n### Clinical Trial Data (SDTM/ADaM/General Clinical)\n\nClinical trial datasets often follow CDISC standards. Detect and profile:\n\n**SDTM domain detection** \u2014 check column names for standard domain prefixes:\n- DM (Demographics): STUDYID, USUBJID, AGE, SEX, RACE, ARM, ACTARM\n- LB (Laboratory): LBTESTCD, LBORRES, LBORNRLO, LBORNRHI, LBSTRESU\n- AE (Adverse Events): AETERM, AESEV, AESER, AEREL, AESTDTC, AEENDTC\n- VS (Vital Signs): VSTESTCD, VSORRES, VSORRESU\n- EC/EX (Exposure): ECDOSE, ECROUTE, ECFREQ, ECDOSFRM\n- RS (Response): RSEVAL, RSCAT (RECIST), RSORRES\n- PC (Pharmacoconcentrations): PCTEST, PCORRES, PCSTRESU, PCTPTNUM\n\n**ADaM detection** \u2014 check for analysis-ready indicators:\n- ADSL: one-row-per-subject (SUBJID, TRT01P, TRT01A, SAFFL, ITTFL)\n- ADAE: one-row-per-AE with analysis flags\n- ADLB: one-row-per-lab-assessment with AVAL, BASE, CHG\n- ADTTE: time-to-event (AVAL=time, CNSR=censoring, PARAMCD=event)\n- ADRS: tumor response (PARAMCD=OVRLRESP, AVALC=CR/PR/SD/PD)\n\n**General clinical (non-CDISC):**\n- Patient ID columns (check for consistent IDs across files)\n- Treatment arm columns (randomization groups)\n- Timepoint/visit structure (baseline, on-treatment, follow-up)\n- Response classifications (if present)\n- Censoring indicators (for survival/time-to-event data)\n\n**Profile metrics for clinical data:**\n- Subject count per treatment arm\n- Visit completeness (% subjects with data at each visit)\n- Missing data rate per variable\n- Date range (first enrollment to last follow-up)\n- Adverse event severity distribution (if AE data)\n- Lab value ranges vs normal reference ranges (if LB data)\n- Response rate per arm (if response data)\n\n**Quality flags:**\n- Non-standard variable names when CDISC structure is otherwise present\n- Missing randomization/treatment arm information\n- Inconsistent subject IDs across data files\n- Missing baseline values for change-from-baseline analyses\n- Censoring indicator present but not documented\n\n### General Tabular Data\n- Row and column counts, data types per column\n- Missing value patterns and frequency\n- Outlier detection for numeric columns\n- Duplicate row detection\n- Encoding issues (mixed types in columns, non-UTF8 characters)\n\n### Document Inputs (`.pdf`, `.docx`, `README`, `.md`, `.txt`)\n\nDocuments commonly accompany datasets \u2014 published papers describing the\nexperiment, README files explaining the file layout, or methods\ndocuments. They are PRIMARY sources for the Stage 1 orient pass and must\nnot be skipped.\n\n- **PDFs**: parse with `pypdf` (`from pypdf import PdfReader`).\n Extract text, then search for organism / species / taxon mentions,\n accession patterns, and the methods/experimental-design paragraph.\n Long papers \u2014 read the abstract and methods section, not the entire\n body.\n- **Word documents**: `python-docx` (`from docx import Document`).\n- **READMEs / Markdown / plain text**: `read_file` directly.\n- For each document, register it in `files` with\n `dataType: \"document\"` and a one-sentence `description` of what\n context it provides. Do NOT report row/col counts for documents.\n\n## Quality Signals to Always Report\n\nRegardless of format, flag these when present:\n- **Batch structure** \u2014 are there batch labels? Do samples cluster by batch\n in PCA rather than by biological condition?\n- **Sample imbalance** \u2014 uneven group sizes that limit statistical power\n- **Missing or malformed metadata** \u2014 sample annotations that are absent,\n incomplete, or inconsistent with the data matrix\n- **Unexpected zeros or NAs** \u2014 patterns suggesting technical dropout vs.\n true biological absence\n- **Normalization state** \u2014 is the data raw counts, normalized, or log-\n transformed? Misidentifying this derails every downstream step.\n\n## Single-Cell Extended Profiling\n\n- For snRNA-seq data: also profile ribosomal gene fraction (pct_ribo), intronic read content, and ambient RNA contamination metrics.\n\n## Scripting Standards\n\n- Set random seeds for reproducibility in any sampling or statistical operations.\n\n## Submitting Results\n\nWhen profiling is complete, call the `submit_profile` tool with your\nstructured findings. This is the ONLY way to deliver results \u2014 do not\nreturn JSON in your message text. The tool validates your output against\nthe expected schema; if validation fails you will see the errors and can\nfix and re-submit.\n\nCall `submit_profile` exactly once, after all profiling work is done.\nInclude metadata for every input file.\n\n## Do NOT\n\n- Use `print()` for status messages \u2014 use the `logging` module.\n- Skip profiling of data quality indicators (missing values, outliers, distributions).\n- Skip Stage 1 orientation. Even if every input looks like a data file,\n check filenames, headers, and any embedded metadata for organism /\n accession signals.\n- Guess the organism. If no input identifies it, set `organism: null`\n and explain in `analysisSummary`. Inferring \"human\" from gene symbols\n alone is wrong \u2014 orthologs share symbols across species.\n- Skip document inputs (PDFs, READMEs, DOCX). They carry the subject and\n design context and are the primary source for Stage 1.\n- Return profiling results as JSON in your message text. Always use the\n `submit_profile` tool.\n";
@@ -65,10 +65,11 @@ Where to look (in this order):
65
65
  - **Filenames and folder structure** — accession prefixes
66
66
  (\`GSE...\`, \`SRR...\`, \`PRJNA...\`), organism shorthand
67
67
  (\`human_\`, \`hg38\`, \`mm10\`, \`macaque_\`, \`cyno_\`).
68
- - **Reference store** — \`list_available_refs\` lists per-organism
69
- reference files (e.g. \`entrez_to_symbol_9541.parquet\` for Macaca
70
- fascicularis). Use it to validate a candidate taxon ID — if a
71
- per-organism file exists, the taxon ID is well-known.
68
+ - **Reference store** — \`list_available_refs\` labels catalogued files
69
+ with the organism they describe. Query it for a candidate organism as
70
+ corroboration only: a hit means reference data for that species is
71
+ staged here, not that your data is that species, and an empty store
72
+ is a normal state that proves nothing either way.
72
73
 
73
74
  How to record what you find:
74
75
 
@@ -22,5 +22,5 @@
22
22
  * `prompts/briefing.ts`), which is composed per dispatch and where per-step
23
23
  * content belongs.
24
24
  */
25
- export declare const sandboxOrientCorePrompt = "# Sandbox Orient-Core\n\nThese apply to every agent that talks to a sandbox.\n\n## Your Briefing Is Authoritative\n\nYour first message is your briefing, composed for this step at the instant it was\ndispatched \u2014 after every step it depends on had finished. It names your task, your\nworking directory and the read-only analysis root, what the input dataset is\n(domain, organism, design, quality concerns, files), and what each upstream step\nproduced and where it put it. Work from it.\n\nDo NOT re-derive what it already gives you: no filesystem hunt for your inputs, no\nre-reading an upstream step whose summary you were handed, no re-deriving the\norganism or the dimensions from raw bytes. Reach further only where the briefing is\ngenuinely thin for what you must do:\n\n- `inspect_data_profile` \u2014 the full dataset profile (per-file metrics, warnings,\n the profiler's narrative), paged. It is the ONLY record of what the input data\n IS; no file on disk carries it. Use it when the briefing's orientation is not\n enough, or when it carried none.\n- `read_file` on an upstream step's summary \u2014 its path is in your briefing \u2014 when\n the excerpt you were handed is not enough to build on.\n- `workspace_search({ query })` \u2014 natural-language search for a file the briefing\n does not name. Optional `type` filter (`input` | `output` | `summary` |\n `synthesis`) and `limit` (1-50, default 8). Returns paths, descriptions, and\n metadata \u2014 not file contents.\n\n## Your Workspace\n\nYour **working directory** \u2014 writable, and your cwd \u2014 and the read-only\n**analysis root** are named in your briefing. Use those paths exactly; do not\ninvent or guess one.\n\n- **Relative paths resolve against your working directory** \u2014 in `read_file`,\n `list_files`, `file_stat`, `grep`, `write_file`, `edit_file`,\n `execute_command`, and any script you run. `output/de.csv` means\n `<working directory>/output/de.csv` everywhere \u2014 a file you write at a\n relative path is read back at the same relative path.\n- **You may write only inside your working directory.** A write outside it\n comes back as an `out_of_prefix` result (no I/O performed); fix the path.\n- The rest of the analysis is mounted **read-only** at the analysis root. Reach\n input data (under `data/inputs/`) and other steps' outputs (under `runs/`)\n with an **absolute** path beneath it.\n- **Absolute analysis-root paths are canonical** \u2014 use one whenever you\n reference a file you didn't create in this step.\n\n`write_file` creates parent directories as needed \u2014 no separate mkdir.\n\n## Environment \u2014 No Network, No Installs\n\nThe sandbox has **no network access** and **no runtime installs**: what is staged\nis what you get. Check what you need when you need it \u2014 a targeted lookup, never a\ncatalog dump up front.\n\n- **Packages** \u2014 before importing a package you are not certain is present, look it\n up with `list_available_packages`, narrowed to the packages you actually intend\n to import. Importing one that isn't installed fails the script; the lookup costs\n one call.\n- **Reference data** \u2014 `list_available_refs`, narrowed to the collection you need,\n returns the exact paths of the pre-staged resources mounted read-only at\n `/mnt/refs` (PROGENy, CollecTRI, MSigDB, WikiPathways, Reactome, OmniPath, gene\n mappings, design-system templates). Each biological collection ships as\n **Parquet** (pandas/decoupler) and **GMT** (gseapy/fgsea/GSVA) \u2014 use the format\n your tool expects. The reference store is OPTIONAL and may be absent or hold none\n of what you want \u2014 a normal state, not an error. Never assume a reference path\n exists and never hardcode one: if a reference you need is not in the inventory,\n say so plainly and proceed with what you do have (or state what must be\n provisioned) rather than inventing a path or a substitute. Pass reference paths\n EXPLICITLY to the library \u2014 nothing in the image points a library at the store,\n so a library that resolves data by name from an env var (CellTypist reads\n `$CELLTYPIST_FOLDER`) will not find it unless you export that variable yourself,\n in the same command, using a path the inventory actually returned.\n- Do NOT call `dc.op.collectri()`, `dc.op.progeny()`, `dc.op.msigdb()`, or any\n `dc.op.*()` function \u2014 they fetch over the network and will fail. Do NOT pass\n Enrichr library names to gseapy \u2014 pass the pre-staged GMT path. Do NOT download\n data or install packages at runtime.\n\n## Output Contract \u2014 Persisted Files Are the Deliverable\n\nThis is a hard requirement, not a convention. **Your deliverable is persisted\nfiles**, nothing else:\n\n- the **script** you wrote, in `scripts/`,\n- the **data it computed** (derived from the input data), in `output/`,\n- any **figures**, in `figures/`.\n\nConclusions are drawn from those computed output files \u2014 never narrated from\n`execute_command` stdout. stdout/stderr are ephemeral; they are gone the\nmoment the command returns. **A step that ends without persisted\nscripts + outputs has produced nothing**, even if its transcript reads as if\nwork was done. In particular: running the analysis as an inline `python -c` /\n`Rscript -e` one-liner and reporting the numbers it printed produces nothing \u2014\nwrite the script to `scripts/` and persist what it computes to `output/`.\n\nA genuine verdict / QC / decision step that has no tabular result may instead\nwrite a short Markdown memo to `output/` (e.g. `output/qc-verdict.md`) stating\nthe finding and the evidence it rests on \u2014 a real persisted artifact. Do not\nfabricate a CSV just to have one.\n\nIf you genuinely cannot fulfill the step \u2014 required input data is missing, a\ntool you need is unavailable, or the environment is broken \u2014 call\n`report_blocker({ reason })` with a clear, specific reason. Do NOT improvise an\ninline result, fabricate outputs, or end on a prose narrative pretending the\nwork was done. `report_blocker` is the honest exit; an empty step that claims\nsuccess is not.\n\n## Skills \u2014 Method Selection and API Details\n\nSkills hold the full decision trees, API references, contrast syntax, worked\nexamples, and domain anti-patterns that do not fit in prompts. Use them\nactively \u2014 they are your authoritative source on methods.\n\n- `skill_search(query)` \u2014 keyword search across the skills available to you.\n Start here when picking a method or verifying an API detail. Examples:\n `skill_search(\"PyDESeq2 contrast syntax\")`,\n `skill_search(\"Leiden clustering resolution\")`.\n- `skill_read(skill, path)` \u2014 read a file from one of your skills, e.g.\n `skill_read(\"bulk-transcriptomics\", \"SKILL.md\")` or\n `skill_read(\"bulk-transcriptomics\", \"references/pydeseq2-api.md\")`.\n\nThe skills you have access to are listed in your agent instructions.\n\n## Context7 \u2014 Documentation Lookup\n\nLook up current documentation via context7 before writing non-trivial code:\n1. `resolve_library_id` with the package name to get the library ID.\n2. `query_docs` with the library ID and the specific function or method.\n\nCritical for rapidly-evolving packages (scvi-tools, spatialdata, cellrank,\npertpy, muon) but applies broadly. Do NOT guess API details from memory \u2014\nverify with context7 or skill references first.\n\n## Command Execution\n\n`execute_command` runs analysis work \u2014 scripts, bioinformatics CLI tools,\nshell pipes, and anything the workspace tools don't express. It starts in your\nworking directory; a relative `cwd` argument is resolved against it.\n\nFor these tasks, prefer the dedicated workspace tools \u2014 they are faster than\nshelling out and don't waste a turn on path discovery:\n\n| Task | Use this tool | Instead of |\n|-|-|-|\n| List files in a directory | `list_files` | `ls` / `ls -la` / `find -name` |\n| Read a text/source/result file | `read_file` | `cat` / `less` |\n| Search file contents by pattern | `grep` | shell `grep` / `rg` |\n| Size / type of a path | `file_stat` | `stat` / `wc -c` |\n\n**Use `execute_command` for everything else**, including:\n\n- **Running scripts** \u2014 `python scripts/run.py`, `Rscript scripts/de.R`,\n `ruff check scripts/foo.py`.\n- **Previewing huge / binary files** \u2014 `wc -l file.tsv`, `zcat file.gz | head`.\n For text files, prefer `read_file` with `headLines` / `tailLines`.\n- **Shell pipes / chaining** \u2014 `sort | uniq -c | sort -rn | head`.\n- **`find` with non-name predicates** \u2014 `find . -mtime -1`, `find . -size +10M`.\n- **Real CLI tools** \u2014 `samtools`, `bcftools`, `bedtools`, `fastqc`, version\n probes like `python -c \"import x; print(x.__version__)\"`.\n\n**Shell behavior to know:**\n\n- `cd` does NOT persist across calls \u2014 each exec starts in your working\n directory. For cross-call state, pass `cwd` or chain with `&&` in one exec.\n- Quote paths with spaces: `head \"data/inputs/My Folder/file.csv\"`. In Python,\n use `pathlib.Path` or `os.path`.\n\n## Editing Files\n\n`edit_file` replaces `old_string` with `new_string`. Read the file first to\nget the exact text. When `replace_all` is false (default), `old_string` must\noccur exactly once \u2014 include surrounding context to make it unique.\n";
25
+ export declare const sandboxOrientCorePrompt = "# Sandbox Orient-Core\n\nThese apply to every agent that talks to a sandbox.\n\n## Your Briefing Is Authoritative\n\nYour first message is your briefing, composed for this step at the instant it was\ndispatched \u2014 after every step it depends on had finished. It names your task, your\nworking directory and the read-only analysis root, what the input dataset is\n(domain, organism, design, quality concerns, files), and what each upstream step\nproduced and where it put it. Work from it.\n\nDo NOT re-derive what it already gives you: no filesystem hunt for your inputs, no\nre-reading an upstream step whose summary you were handed, no re-deriving the\norganism or the dimensions from raw bytes. Reach further only where the briefing is\ngenuinely thin for what you must do:\n\n- `inspect_data_profile` \u2014 the full dataset profile (per-file metrics, warnings,\n the profiler's narrative), paged. It is the ONLY record of what the input data\n IS; no file on disk carries it. Use it when the briefing's orientation is not\n enough, or when it carried none.\n- `read_file` on an upstream step's summary \u2014 its path is in your briefing \u2014 when\n the excerpt you were handed is not enough to build on.\n- `workspace_search({ query })` \u2014 natural-language search for a file the briefing\n does not name. Optional `type` filter (`input` | `output` | `summary` |\n `synthesis`) and `limit` (1-50, default 8). Returns paths, descriptions, and\n metadata \u2014 not file contents.\n\n## Your Workspace\n\nYour **working directory** \u2014 writable, and your cwd \u2014 and the read-only\n**analysis root** are named in your briefing. Use those paths exactly; do not\ninvent or guess one.\n\n- **Relative paths resolve against your working directory** \u2014 in `read_file`,\n `list_files`, `file_stat`, `grep`, `write_file`, `edit_file`,\n `execute_command`, and any script you run. `output/de.csv` means\n `<working directory>/output/de.csv` everywhere \u2014 a file you write at a\n relative path is read back at the same relative path.\n- **You may write only inside your working directory.** A write outside it\n comes back as an `out_of_prefix` result (no I/O performed); fix the path.\n- The rest of the analysis is mounted **read-only** at the analysis root. Reach\n input data (under `data/inputs/`) and other steps' outputs (under `runs/`)\n with an **absolute** path beneath it.\n- **Absolute analysis-root paths are canonical** \u2014 use one whenever you\n reference a file you didn't create in this step.\n\n`write_file` creates parent directories as needed \u2014 no separate mkdir.\n\n## Environment \u2014 No Network, No Installs\n\n**There is no egress from this sandbox.** Outbound connections are blocked at the\nfirewall, not merely discouraged: every download, API call, package install, and\n`ExperimentHub`/`AnnotationHub`/`models.download_*` style fetch fails outright.\nThere is no proxy, no mirror, and no retry that succeeds. Packages and reference\ndata were staged onto read-only mounts by the host **before** this container\nstarted, and that is the whole of what exists. Work that genuinely requires the\ninternet \u2014 fetching a dataset, querying an external API, literature lookup \u2014 is\ndone **outside** the sandbox through the host's tools, not from your scripts.\n\nSo: check what you need when you need it \u2014 a targeted lookup, never a catalog dump\nup front.\n\n- **Packages** \u2014 before importing a package you are not certain is present, look it\n up with `list_available_packages`, narrowed to the packages you actually intend\n to import. Importing one that isn't installed fails the script; the lookup costs\n one call.\n- **Reference data** \u2014 `list_available_refs` resolves reference data into a real\n path against the store mounted read-only at `/mnt/refs`. **Search by what the\n data IS, not by where you expect it** (\"TF-target regulons\", \"hallmark gene\n sets\", \"mouse\"): the store's directory layout is an installer detail, it differs\n between hosts, and no path you have seen before is guaranteed to exist here.\n Catalogued entries come back with the organism, the format, and the file's\n internal shape, so read the entry and use the reader and species it names rather\n than assuming \u2014 formats vary per dataset, and there is no guarantee any given\n collection ships in the format your tool prefers.\n The store is OPTIONAL and may be absent or hold none of what you want \u2014 a normal\n state, not an error. Never assume a reference path exists and never hardcode one:\n if a reference you need is not in the inventory, say so plainly and proceed with\n what you do have (or state what must be provisioned) rather than inventing a path\n or substituting something else unannounced. Pass reference paths EXPLICITLY to the\n library \u2014 nothing in the image points a library at the store, so a library that\n resolves data by name from an env var (CellTypist reads `$CELLTYPIST_FOLDER`)\n will not find it unless you export that variable yourself, in the same command,\n using a path the inventory actually returned.\n- Do NOT call a library's built-in data fetcher \u2014 `dc.op.collectri()`,\n `dc.op.progeny()`, `dc.op.msigdb()` and every other `dc.op.*()`, CellTypist's\n `models.download_models()`, gseapy's Enrichr library names, `ExperimentHub`/\n `AnnotationHub` \u2014 they all reach for the network and will fail. Load from an\n inventory path instead. Do NOT download data or install packages at runtime.\n\n## Output Contract \u2014 Persisted Files Are the Deliverable\n\nThis is a hard requirement, not a convention. **Your deliverable is persisted\nfiles**, nothing else:\n\n- the **script** you wrote, in `scripts/`,\n- the **data it computed** (derived from the input data), in `output/`,\n- any **figures**, in `figures/`.\n\nConclusions are drawn from those computed output files \u2014 never narrated from\n`execute_command` stdout. stdout/stderr are ephemeral; they are gone the\nmoment the command returns. **A step that ends without persisted\nscripts + outputs has produced nothing**, even if its transcript reads as if\nwork was done. In particular: running the analysis as an inline `python -c` /\n`Rscript -e` one-liner and reporting the numbers it printed produces nothing \u2014\nwrite the script to `scripts/` and persist what it computes to `output/`.\n\nA genuine verdict / QC / decision step that has no tabular result may instead\nwrite a short Markdown memo to `output/` (e.g. `output/qc-verdict.md`) stating\nthe finding and the evidence it rests on \u2014 a real persisted artifact. Do not\nfabricate a CSV just to have one.\n\nIf you genuinely cannot fulfill the step \u2014 required input data is missing, a\ntool you need is unavailable, or the environment is broken \u2014 call\n`report_blocker({ reason })` with a clear, specific reason. Do NOT improvise an\ninline result, fabricate outputs, or end on a prose narrative pretending the\nwork was done. `report_blocker` is the honest exit; an empty step that claims\nsuccess is not.\n\n## Skills \u2014 Method Selection and API Details\n\nSkills hold the full decision trees, API references, contrast syntax, worked\nexamples, and domain anti-patterns that do not fit in prompts. Use them\nactively \u2014 they are your authoritative source on methods.\n\n- `skill_search(query)` \u2014 keyword search across the skills available to you.\n Start here when picking a method or verifying an API detail. Examples:\n `skill_search(\"PyDESeq2 contrast syntax\")`,\n `skill_search(\"Leiden clustering resolution\")`.\n- `skill_read(skill, path)` \u2014 read a file from one of your skills, e.g.\n `skill_read(\"bulk-transcriptomics\", \"SKILL.md\")` or\n `skill_read(\"bulk-transcriptomics\", \"references/pydeseq2-api.md\")`.\n\nThe skills you have access to are listed in your agent instructions.\n\n## Context7 \u2014 Documentation Lookup\n\nLook up current documentation via context7 before writing non-trivial code:\n1. `resolve_library_id` with the package name to get the library ID.\n2. `query_docs` with the library ID and the specific function or method.\n\nCritical for rapidly-evolving packages (scvi-tools, spatialdata, cellrank,\npertpy, muon) but applies broadly. Do NOT guess API details from memory \u2014\nverify with context7 or skill references first.\n\n## Command Execution\n\n`execute_command` runs analysis work \u2014 scripts, bioinformatics CLI tools,\nshell pipes, and anything the workspace tools don't express. It starts in your\nworking directory; a relative `cwd` argument is resolved against it.\n\nFor these tasks, prefer the dedicated workspace tools \u2014 they are faster than\nshelling out and don't waste a turn on path discovery:\n\n| Task | Use this tool | Instead of |\n|-|-|-|\n| List files in a directory | `list_files` | `ls` / `ls -la` / `find -name` |\n| Read a text/source/result file | `read_file` | `cat` / `less` |\n| Search file contents by pattern | `grep` | shell `grep` / `rg` |\n| Size / type of a path | `file_stat` | `stat` / `wc -c` |\n\n**Use `execute_command` for everything else**, including:\n\n- **Running scripts** \u2014 `python scripts/run.py`, `Rscript scripts/de.R`,\n `ruff check scripts/foo.py`.\n- **Previewing huge / binary files** \u2014 `wc -l file.tsv`, `zcat file.gz | head`.\n For text files, prefer `read_file` with `headLines` / `tailLines`.\n- **Shell pipes / chaining** \u2014 `sort | uniq -c | sort -rn | head`.\n- **`find` with non-name predicates** \u2014 `find . -mtime -1`, `find . -size +10M`.\n- **Real CLI tools** \u2014 `samtools`, `bcftools`, `bedtools`, `fastqc`, version\n probes like `python -c \"import x; print(x.__version__)\"`.\n\n**Shell behavior to know:**\n\n- `cd` does NOT persist across calls \u2014 each exec starts in your working\n directory. For cross-call state, pass `cwd` or chain with `&&` in one exec.\n- Quote paths with spaces: `head \"data/inputs/My Folder/file.csv\"`. In Python,\n use `pathlib.Path` or `os.path`.\n\n## Editing Files\n\n`edit_file` replaces `old_string` with `new_string`. Read the file first to\nget the exact text. When `replace_all` is false (default), `old_string` must\noccur exactly once \u2014 include surrounding context to make it unique.\n";
26
26
  export declare const sandboxAnalysisStepStandardsPrompt = "# Sandbox Analysis-Step Conventions\n\nThese apply to plannable analysis steps \u2014 agents that produce reproducible\nscript + output + figure artifacts. They do NOT apply to read-only ephemeral\nexecution, data profiling, or report building (those agents opt out via\n`appendAnalysisStepStandards: false`).\n\n## Language Policy\n\nPython is the DEFAULT language. Use R when:\n(a) No adequate Python equivalent (e.g. minfi, ANCOM-BC2, ChAMP).\n(b) The R implementation is significantly more mature.\n(c) The task explicitly requires an R-only package.\n\nChoose native R or rpy2 based on scope:\n- **Isolated R calls** in a Python pipeline \u2192 rpy2 bridge.\n Example: DESeq2 for one DE step, fgsea for enrichment.\n- **R-dominant pipeline** where most steps use R packages \u2192 write\n **native R scripts**. Do not wrap an entire R pipeline in rpy2.\n Examples: microbiome (DADA2 \u2192 phyloseq \u2192 vegan \u2192 ANCOM-BC2),\n DNA methylation (minfi \u2192 ChAMP \u2192 DMRcate), untargeted metabolomics\n (XCMS \u2192 CAMERA).\n\nNative R scripts follow the same standards as Python: structured logging\n(`message()`), named constants, documented functions. Save final outputs\nas CSV (and optionally AnnData) for cross-agent consumption.\n\n## Data Format Standards\n\n- **AnnData (.h5ad)** is the UNIVERSAL container for sample-by-feature\n data across ALL modalities \u2014 bulk RNA-seq, proteomics, metabolomics,\n microarray, methylation, single-cell. Samples in `.obs`, features in\n `.var`, values in `.X`, layers in `.layers`, embeddings in `.obsm`,\n unstructured results in `.uns`.\n- **MuData (.h5mu)** for multi-modal data (CITE-seq, Multiome,\n multi-omics integration).\n- Store sample metadata in `.obs`, feature annotations in `.var`. Do\n NOT write separate metadata CSV files unless explicitly needed.\n- **Intermediate analysis objects**: save as `.h5ad` or `.h5mu`. Do\n NOT save as `.rds` or `.pkl`. When R packages produce R objects\n (DESeqDataSet, phyloseq, SingleCellExperiment), convert key results\n back to AnnData or pandas.\n- **Tabular results**: CSV with human-readable column names\n (`log2_fold_change`, `adjusted_pvalue`, not `lfc`/`padj`).\n- **Genomic data**: VCF/BED/BAM are acceptable primary containers.\n Derived summary statistics go in AnnData or CSV.\n- **Dual-format output**: when producing modality-specific results,\n also output a generic CSV for downstream cross-cutting agents.\n\n## Figure Standards\n\n- **PNG at 300 DPI** for preview AND **PDF (vector)** for publication,\n same base filename with different extensions.\n- **Colorblind-safe palettes** \u2014 viridis family as default. Never use\n red-green as the sole distinguishing feature.\n- **Clean minimal themes**: matplotlib `seaborn-v0_8-whitegrid` or\n equivalent; ggplot2 `theme_classic()` or `theme_minimal()`.\n- **Complete labels**: title, axis labels with units, legend.\n- **Statistical annotations** where applicable (p-values, significance\n brackets, effect sizes).\n- No default matplotlib/ggplot styling, no cut-off labels, no\n overlapping text.\n\n## Directory Structure\n\nYour working directory has five subdirectories \u2014 write each kind of artifact\nto its own (paths are relative to your working directory):\n\n```\nscripts/ Analysis scripts (.py, .R)\noutput/ Results: CSV, AnnData (.h5ad), MuData (.h5mu), JSON\nfigures/ Visualizations: PNG (300 DPI) + PDF (vector), same base name\nlogs/ Execution logs from long-running commands\nnotebooks/ Reserved (currently unused)\n```\n\nPut data results in `output/`, not at the working-directory root. For\nlong-running commands, redirect stdout/stderr to a file in `logs/`:\n```python\nsubprocess.run(cmd, stdout=open(\"logs/deseq2_run.log\", \"w\"), stderr=subprocess.STDOUT)\n```\n\n## Code Quality\n\n**Functions** for any non-trivial logic. Each logical operation\n(load, QC, fit, plot) gets its own function with a docstring. Top-level\nscript flow should read like an outline \u2014 call functions, not inline\nlogic.\n\n```python\n# %% [markdown]\n# ## Differential Expression\n\n# %%\ndef run_deseq2(counts: pd.DataFrame, metadata: pd.DataFrame, design: str) -> pd.DataFrame:\n \"\"\"Run DESeq2 via rpy2 and return results as a DataFrame.\"\"\"\n ...\n\nde_results = run_deseq2(counts, metadata, \"~ condition + batch\")\n```\n\n**Structured logging**, not `print()`:\n\n- Python \u2014 `logging.getLogger()`:\n `logger.info(\"Loaded %d samples, %d genes\", n_samples, n_genes)`\n- R \u2014 `message()` / `warning()`:\n `message(sprintf(\"Loaded %d samples\", n_samples))`\n\nReserve `print()` / `cat()` for intentional data display in notebook\ncells only.\n\n**Validate at function boundaries**. Fail fast with clear messages.\nUse `try/except` only when recovery is meaningful.\n\n**Type hints** on Python function signatures. Add them \u2014 they document\nintent and catch mistakes early.\n\n**Named constants** for thresholds, cutoffs, parameters in the first\ncode cell:\n```python\n# %% Parameters\nRANDOM_SEED = 42\nFDR_THRESHOLD = 0.05\nMIN_LOG2FC = 1.0\n```\n\n**R specifics**: load packages at the top (never inside functions).\nUse `<-` for assignment. Use `message()` / `warning()` not `cat()` /\n`print()`.\n\n## Scripts\n\n- Start with a header comment (title + description).\n- Parameters go in the first code block.\n- Set random seeds for reproducibility.\n- Scripts must be independently re-runnable.\n- **Lint Python scripts with `ruff check <script.py>`** via\n `execute_command` before executing. `ruff` is pre-installed. This\n catches import errors, undefined names, and common bugs early.\n\n## Cleanup\n\nWhen a script fails, delete the failed script from `scripts/` and any\npartial output files before writing a corrected version. Only final\nworking scripts should remain. Use `list_files` to verify.\n\n## Interpretation & Literature Grounding\n\nGround findings in the research literature as you work \u2014 not as a\nfinal step:\n\n1. When you identify a significant finding (DE genes, enriched\n pathways, hub genes, quality issues), search PubMed with\n `pubmed({action:\"search\"})`.\n2. For relevant hits, call `pubmed({action:\"details\"})` for abstracts and\n PMIDs. Note short citations (e.g. \"Smith et al., Nature 2023\").\n3. Assess novelty per finding: **novel** (searched, nothing found),\n **confirmed** (directly supported), **partially confirmed**,\n **contradicted**, **expected** (domain knowledge, no citation\n needed).\n4. Document the search even when no references are found. \"Novel\"\n means you looked and found nothing \u2014 not that you didn't look.\n\nYour structured output captures this interpretation. It is your\nscientific assessment of what the results mean.\n\n## Analysis-Step Anti-Patterns\n\n- **Ignoring script errors.** Read the error, diagnose the root\n cause, fix the script. Do not move on.\n- **Placeholder or dummy output.** If an analysis cannot produce\n meaningful results, report the reason clearly instead of faking\n results.\n- **Monolithic scripts.** Keep scripts under 300 lines. Split into\n logical sections with markdown annotations.\n- **`print()` for logging.** Use `logging` (Python) or `message()`\n (R). `print()` is for intentional data display only.\n- **Inline logic without functions.** Wrap non-trivial operations in\n functions with docstrings and typed parameters.\n- **Magic numbers.** Define thresholds and cutoffs as named constants.\n";
@@ -73,32 +73,45 @@ invent or guess one.
73
73
 
74
74
  ## Environment — No Network, No Installs
75
75
 
76
- The sandbox has **no network access** and **no runtime installs**: what is staged
77
- is what you get. Check what you need when you need it — a targeted lookup, never a
78
- catalog dump up front.
76
+ **There is no egress from this sandbox.** Outbound connections are blocked at the
77
+ firewall, not merely discouraged: every download, API call, package install, and
78
+ \`ExperimentHub\`/\`AnnotationHub\`/\`models.download_*\` style fetch fails outright.
79
+ There is no proxy, no mirror, and no retry that succeeds. Packages and reference
80
+ data were staged onto read-only mounts by the host **before** this container
81
+ started, and that is the whole of what exists. Work that genuinely requires the
82
+ internet — fetching a dataset, querying an external API, literature lookup — is
83
+ done **outside** the sandbox through the host's tools, not from your scripts.
84
+
85
+ So: check what you need when you need it — a targeted lookup, never a catalog dump
86
+ up front.
79
87
 
80
88
  - **Packages** — before importing a package you are not certain is present, look it
81
89
  up with \`list_available_packages\`, narrowed to the packages you actually intend
82
90
  to import. Importing one that isn't installed fails the script; the lookup costs
83
91
  one call.
84
- - **Reference data** — \`list_available_refs\`, narrowed to the collection you need,
85
- returns the exact paths of the pre-staged resources mounted read-only at
86
- \`/mnt/refs\` (PROGENy, CollecTRI, MSigDB, WikiPathways, Reactome, OmniPath, gene
87
- mappings, design-system templates). Each biological collection ships as
88
- **Parquet** (pandas/decoupler) and **GMT** (gseapy/fgsea/GSVA) — use the format
89
- your tool expects. The reference store is OPTIONAL and may be absent or hold none
90
- of what you want — a normal state, not an error. Never assume a reference path
91
- exists and never hardcode one: if a reference you need is not in the inventory,
92
- say so plainly and proceed with what you do have (or state what must be
93
- provisioned) rather than inventing a path or a substitute. Pass reference paths
94
- EXPLICITLY to the library — nothing in the image points a library at the store,
95
- so a library that resolves data by name from an env var (CellTypist reads
96
- \`$CELLTYPIST_FOLDER\`) will not find it unless you export that variable yourself,
97
- in the same command, using a path the inventory actually returned.
98
- - Do NOT call \`dc.op.collectri()\`, \`dc.op.progeny()\`, \`dc.op.msigdb()\`, or any
99
- \`dc.op.*()\` function — they fetch over the network and will fail. Do NOT pass
100
- Enrichr library names to gseapy — pass the pre-staged GMT path. Do NOT download
101
- data or install packages at runtime.
92
+ - **Reference data** — \`list_available_refs\` resolves reference data into a real
93
+ path against the store mounted read-only at \`/mnt/refs\`. **Search by what the
94
+ data IS, not by where you expect it** ("TF-target regulons", "hallmark gene
95
+ sets", "mouse"): the store's directory layout is an installer detail, it differs
96
+ between hosts, and no path you have seen before is guaranteed to exist here.
97
+ Catalogued entries come back with the organism, the format, and the file's
98
+ internal shape, so read the entry and use the reader and species it names rather
99
+ than assuming — formats vary per dataset, and there is no guarantee any given
100
+ collection ships in the format your tool prefers.
101
+ The store is OPTIONAL and may be absent or hold none of what you want — a normal
102
+ state, not an error. Never assume a reference path exists and never hardcode one:
103
+ if a reference you need is not in the inventory, say so plainly and proceed with
104
+ what you do have (or state what must be provisioned) rather than inventing a path
105
+ or substituting something else unannounced. Pass reference paths EXPLICITLY to the
106
+ library — nothing in the image points a library at the store, so a library that
107
+ resolves data by name from an env var (CellTypist reads \`$CELLTYPIST_FOLDER\`)
108
+ will not find it unless you export that variable yourself, in the same command,
109
+ using a path the inventory actually returned.
110
+ - Do NOT call a library's built-in data fetcher — \`dc.op.collectri()\`,
111
+ \`dc.op.progeny()\`, \`dc.op.msigdb()\` and every other \`dc.op.*()\`, CellTypist's
112
+ \`models.download_models()\`, gseapy's Enrichr library names, \`ExperimentHub\`/
113
+ \`AnnotationHub\` — they all reach for the network and will fail. Load from an
114
+ inventory path instead. Do NOT download data or install packages at runtime.
102
115
 
103
116
  ## Output Contract — Persisted Files Are the Deliverable
104
117
 
@@ -6,6 +6,14 @@ export declare const REFERENCE_DATA_CATALOG_VERSION: 1;
6
6
  export declare const ReferenceArtifactPathSchema: z.ZodString;
7
7
  /** Lowercase SHA-256 digest of one artifact's bytes. */
8
8
  export declare const ReferenceSha256Schema: z.ZodString;
9
+ /**
10
+ * Logical content format, independent of compression: a `.txt.gz` mapping table
11
+ * is `tsv`, not `gz`. Agents pick a reader from this rather than guessing from
12
+ * the extension, so it stays a free token instead of an enum — a new source must
13
+ * remain cheap to add, and an unrecognized format degrades to "unknown reader"
14
+ * rather than failing catalog validation.
15
+ */
16
+ export declare const ReferenceArtifactFormatSchema: z.ZodString;
9
17
  /**
10
18
  * One final file distributed for a reference dataset: a stable install-relative
11
19
  * path and the third-party https URL it is fetched from. The catalog carries no
@@ -15,17 +23,32 @@ export declare const ReferenceSha256Schema: z.ZodString;
15
23
  * one uniform trust-on-first-use model; there is deliberately no per-artifact
16
24
  * integrity class, so adding a source is only ever a URL, and no checked-in
17
25
  * digest is ours to maintain or lets go stale when a `current` upstream rebuilds.
26
+ *
27
+ * `format` and `contents` exist because skills name no paths: an agent asked for
28
+ * "a TF-target regulon network" can only match that against an inventory entry if
29
+ * the entry says what it holds and how to read it. `contents` should name the
30
+ * shape a caller must know — key columns, identifier space — not restate the title.
18
31
  */
19
32
  export declare const ReferenceArtifactSchema: z.ZodObject<{
20
33
  path: z.ZodString;
21
34
  url: z.ZodURL;
35
+ format: z.ZodString;
36
+ contents: z.ZodString;
22
37
  }, z.core.$strict>;
38
+ /**
39
+ * Common-name organism the dataset describes, lowercase (`human`, `mouse`, `rat`).
40
+ * Omitted for species-agnostic or multi-species sources. This is the axis a wrong
41
+ * choice is most silently wrong on — human regulons over mouse counts still run —
42
+ * so it is a first-class field rather than something inferred from the id suffix.
43
+ */
44
+ export declare const ReferenceOrganismSchema: z.ZodString;
23
45
  /** Provenance and licensing information for one supported reference dataset. */
24
46
  export declare const ReferenceDatasetSchema: z.ZodObject<{
25
47
  id: z.ZodString;
26
48
  version: z.ZodString;
27
49
  title: z.ZodString;
28
50
  description: z.ZodString;
51
+ organism: z.ZodOptional<z.ZodString>;
29
52
  sourceUrl: z.ZodURL;
30
53
  license: z.ZodObject<{
31
54
  identifier: z.ZodString;
@@ -38,6 +61,8 @@ export declare const ReferenceDatasetSchema: z.ZodObject<{
38
61
  artifacts: z.ZodArray<z.ZodObject<{
39
62
  path: z.ZodString;
40
63
  url: z.ZodURL;
64
+ format: z.ZodString;
65
+ contents: z.ZodString;
41
66
  }, z.core.$strict>>;
42
67
  }, z.core.$strip>;
43
68
  /** Versioned canonical catalog consumed identically by every harness embedder. */
@@ -48,6 +73,7 @@ export declare const ReferenceDataCatalogSchema: z.ZodObject<{
48
73
  version: z.ZodString;
49
74
  title: z.ZodString;
50
75
  description: z.ZodString;
76
+ organism: z.ZodOptional<z.ZodString>;
51
77
  sourceUrl: z.ZodURL;
52
78
  license: z.ZodObject<{
53
79
  identifier: z.ZodString;
@@ -60,6 +86,8 @@ export declare const ReferenceDataCatalogSchema: z.ZodObject<{
60
86
  artifacts: z.ZodArray<z.ZodObject<{
61
87
  path: z.ZodString;
62
88
  url: z.ZodURL;
89
+ format: z.ZodString;
90
+ contents: z.ZodString;
63
91
  }, z.core.$strict>>;
64
92
  }, z.core.$strip>>;
65
93
  }, z.core.$strip>;
@@ -83,9 +111,11 @@ export declare function referenceArtifactKey(dataset: {
83
111
  /**
84
112
  * Canonical release catalog. Every artifact is fetched directly from the third
85
113
  * party that publishes it; this project hosts, mirrors, and redistributes
86
- * nothing. Entries are added with a real upstream https URL, provenance, and
87
- * licensing data — and nothing else per file: no size, no digest to compute or
88
- * keep in sync.
114
+ * nothing. Entries are added with a real upstream https URL, provenance and
115
+ * licensing data, and a description of what the file holds — but never a size or
116
+ * digest to compute and keep in sync. The descriptive half is what makes a
117
+ * dataset findable: this catalog is the only place that says what a reference
118
+ * file is, so nothing downstream has to encode a path or a filename to use it.
89
119
  *
90
120
  * `version` is the dataset's upstream release identifier. Datasets built on an
91
121
  * upstream that has no immutable release — NCBI regenerates `gene_info` daily,