@inflexa-ai/harness 0.32.0 → 0.33.1

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 (47) hide show
  1. package/dist/agents/conversation-agent.d.ts +8 -0
  2. package/dist/agents/conversation-agent.js +15 -6
  3. package/dist/agents/sandbox/shared.d.ts +13 -1
  4. package/dist/agents/sandbox/shared.js +22 -5
  5. package/dist/config/environment-stores.d.ts +53 -5
  6. package/dist/index.d.ts +4 -0
  7. package/dist/index.js +3 -0
  8. package/dist/prompts/briefing.d.ts +1 -1
  9. package/dist/prompts/briefing.js +4 -0
  10. package/dist/prompts/planner.js +13 -0
  11. package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
  12. package/dist/prompts/sandbox/multi-omics-integration-agent.js +5 -5
  13. package/dist/prompts/sandbox/network-agent.d.ts +1 -1
  14. package/dist/prompts/sandbox/network-agent.js +4 -3
  15. package/dist/prompts/sandbox-standards.d.ts +20 -0
  16. package/dist/prompts/sandbox-standards.js +65 -0
  17. package/dist/reference-data/catalog.js +11 -9
  18. package/dist/sandbox/create-sandbox.d.ts +29 -4
  19. package/dist/sandbox/create-sandbox.js +6 -0
  20. package/dist/sandbox/docker-client.d.ts +16 -6
  21. package/dist/sandbox/docker-client.js +47 -31
  22. package/dist/sandbox/farm.d.ts +95 -0
  23. package/dist/sandbox/farm.js +127 -0
  24. package/dist/sandbox/k8s-client.d.ts +24 -5
  25. package/dist/sandbox/k8s-client.js +78 -9
  26. package/dist/sandbox/mount-plan.d.ts +23 -1
  27. package/dist/sandbox/mount-plan.js +44 -7
  28. package/dist/sandbox/sandbox-error.d.ts +12 -0
  29. package/dist/sandbox/sandbox-error.js +2 -0
  30. package/dist/sandbox/types.d.ts +118 -0
  31. package/dist/schemas/plan-schemas.d.ts +3 -0
  32. package/dist/schemas/plan-schemas.js +1 -1
  33. package/dist/schemas/validate-plan.js +20 -1
  34. package/dist/schemas/workflow-state.d.ts +4 -0
  35. package/dist/schemas/workflow-state.js +10 -0
  36. package/dist/tasks/data-profile.d.ts +15 -0
  37. package/dist/tasks/data-profile.js +36 -26
  38. package/dist/tools/execute-analysis.d.ts +18 -0
  39. package/dist/tools/execute-analysis.js +87 -0
  40. package/dist/tools/research/generate-plan.js +12 -2
  41. package/dist/tools/sandbox/index.d.ts +1 -0
  42. package/dist/tools/sandbox/index.js +1 -0
  43. package/dist/tools/sandbox/link-packages.d.ts +27 -0
  44. package/dist/tools/sandbox/link-packages.js +52 -0
  45. package/dist/tools/sandbox/list-available-packages.d.ts +50 -30
  46. package/dist/tools/sandbox/list-available-packages.js +131 -40
  47. package/package.json +2 -2
@@ -29,6 +29,7 @@ import type { AcquireEyes } from "../lib/eyes.js";
29
29
  import type { AgentDefinition } from "../loop/types.js";
30
30
  import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
31
31
  import type { Tool } from "../tools/define-tool.js";
32
+ import type { ExtendAnalysisFarm } from "../sandbox/types.js";
32
33
  import type { WorkspaceFilesystem } from "../workspace/filesystem.js";
33
34
  import type { ResolveWorkspaceRoot } from "../workspace/paths.js";
34
35
  import { type BioToolKeys } from "../tools/bio/keys.js";
@@ -132,6 +133,13 @@ export interface ConversationAgentDeps extends EnvironmentStorePaths {
132
133
  * built-in tool.
133
134
  */
134
135
  readonly hostTools?: readonly Tool[];
136
+ /**
137
+ * The farm-extension seam of the embedder. When it is bound,
138
+ * `execute_analysis` links the packages of the plan into the farm of the
139
+ * analysis before the launch, and a pool miss refuses the launch. Without
140
+ * it, the link pass returns at once.
141
+ */
142
+ readonly extendAnalysisFarm?: ExtendAnalysisFarm;
135
143
  }
136
144
  /** Build the conversation `AgentDefinition` with every tool fully dep-bound. */
137
145
  export declare function createConversationAgent(deps: ConversationAgentDeps): AgentDefinition;
@@ -42,7 +42,7 @@ export const CONVERSATION_AGENT_ID = "conversation-agent";
42
42
  const CONVERSATION_MAX_ITERATIONS = 200;
43
43
  /** Build the conversation `AgentDefinition` with every tool fully dep-bound. */
44
44
  export function createConversationAgent(deps) {
45
- const { provider, pool, embedding, workspaceFs, model, utilityProvider, utilityModel, executeAnalysisWorkflow, runAuthorizer, runLauncher, bioKeys, chrome, resourcePolicy, hostTools, refStorePath, packagesFile, usageRecorder, citationResolver, } = deps;
45
+ const { provider, pool, embedding, workspaceFs, model, utilityProvider, utilityModel, executeAnalysisWorkflow, runAuthorizer, runLauncher, bioKeys, chrome, resourcePolicy, hostTools, refStorePath, farmLockFile, imagePackagesFile, readPoolInventory, usageRecorder, citationResolver, } = deps;
46
46
  const workingMemory = createWorkingMemory(pool);
47
47
  const ncbi = createNcbiTools(bioKeys);
48
48
  const chemDb = createChemDbTools(bioKeys, { ...(deps.logger ? { logger: deps.logger } : {}) });
@@ -86,10 +86,16 @@ export function createConversationAgent(deps) {
86
86
  // blind is how a user gets asked to download something already installed, or
87
87
  // told nothing is missing when it is; this is the only tool that can tell.
88
88
  createListAvailableRefsTool({ ...(refStorePath ? { refStorePath } : {}) }),
89
- // What is importable inside a sandbox. Reads the same manifest a sandbox agent
90
- // reads, host-side — so "is scanpy available?" is a manifest lookup here rather
91
- // than launching analysis computation to run one import.
92
- createListAvailablePackagesTool({ ...(packagesFile ? { packagesFile } : {}) }),
89
+ // What the STORE holds, pool-scope where the embedder binds the reader:
90
+ // the ask flow of this agent marks the packages the pool does not hold,
91
+ // and the farm of a new analysis is empty — a farm view here would read
92
+ // every pool package as absent. The farm-lock fallback keeps a host
93
+ // with no pool reader on the old answer.
94
+ createListAvailablePackagesTool({
95
+ ...(farmLockFile ? { farmLockFile } : {}),
96
+ ...(imagePackagesFile ? { imagePackagesFile } : {}),
97
+ ...(readPoolInventory ? { readPoolInventory } : {}),
98
+ }),
93
99
  // Execution.
94
100
  createInspectRunTool(pool),
95
101
  // The dataset's own record. No file backs it — the DB row is the only copy.
@@ -101,7 +107,9 @@ export function createConversationAgent(deps) {
101
107
  usageRecorder,
102
108
  bioKeys,
103
109
  ...(refStorePath ? { refStorePath } : {}),
104
- ...(packagesFile ? { packagesFile } : {}),
110
+ ...(farmLockFile ? { farmLockFile } : {}),
111
+ ...(imagePackagesFile ? { imagePackagesFile } : {}),
112
+ ...(readPoolInventory ? { readPoolInventory } : {}),
105
113
  ...(deps.logger ? { logger: deps.logger } : {}),
106
114
  }),
107
115
  createExecuteAnalysisTool({
@@ -112,6 +120,7 @@ export function createConversationAgent(deps) {
112
120
  resourcePolicy,
113
121
  utilityProvider,
114
122
  utilityModel,
123
+ ...(deps.extendAnalysisFarm ? { extendAnalysisFarm: deps.extendAnalysisFarm } : {}),
115
124
  ...(deps.logger ? { logger: deps.logger } : {}),
116
125
  }),
117
126
  // The one report path. The tool starts a report thread as a child of the
@@ -19,7 +19,7 @@ import type { Pool } from "pg";
19
19
  import type { AgentDefinition } from "../../loop/types.js";
20
20
  import type { ChatProvider, EmbeddingProvider } from "../../providers/types.js";
21
21
  import type { SandboxClient } from "../../sandbox/client.js";
22
- import type { SandboxRef } from "../../sandbox/types.js";
22
+ import type { ExtendAnalysisFarm, SandboxRef, ToolchainSource } from "../../sandbox/types.js";
23
23
  import type { WorkspaceFilesystem } from "../../workspace/filesystem.js";
24
24
  import type { ProvenanceCollector as LineageCollector } from "../../provenance/collector.js";
25
25
  import { type BioToolKeys } from "../../tools/bio/keys.js";
@@ -84,6 +84,18 @@ export interface SandboxAgentDeps extends EnvironmentStorePaths {
84
84
  * agents that have no terminal status to declare (such as the data profiler).
85
85
  */
86
86
  readonly blockerHolder?: BlockerHolder;
87
+ /**
88
+ * The farm-extension seam of the embedder. When it is bound, every
89
+ * sandbox agent gets the `link_packages` tool in its always-on substrate,
90
+ * and the package-link prompt layer appends. Without it, neither exists.
91
+ */
92
+ readonly extendAnalysisFarm?: ExtendAnalysisFarm;
93
+ /**
94
+ * The declared toolchain owner. Keys the orient-core environment text.
95
+ * Absent keeps the legacy text, thus an old embedder keeps its cached
96
+ * prompt prefix.
97
+ */
98
+ readonly toolchainSource?: ToolchainSource;
87
99
  }
88
100
  /** Per-agent override for the prompt composition and tool surface. */
89
101
  export interface SandboxAgentPromptOptions {
@@ -15,10 +15,10 @@
15
15
  * must map to a concrete `Tool`; unknown names throw at composition time
16
16
  * so misconfigured agents fail at startup, not at the first LLM call.
17
17
  */
18
- import { sandboxOrientCorePrompt, sandboxAnalysisStepStandardsPrompt } from "../../prompts/sandbox-standards.js";
18
+ import { sandboxOrientCorePromptFor, sandboxPackageLinkPrompt, sandboxAnalysisStepStandardsPrompt } from "../../prompts/sandbox-standards.js";
19
19
  import { composeSystemPrompt } from "../system-prompt.js";
20
- // Sandbox-environment introspection.
21
- import { createListAvailablePackagesTool, createListAvailableRefsTool } from "../../tools/sandbox/index.js";
20
+ // Sandbox-environment introspection, plus the farm-extension seam face.
21
+ import { createLinkPackagesTool, 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).
@@ -59,7 +59,13 @@ function resolveSandboxTools(deps, tools) {
59
59
  throw new Error('createSandboxAgent: SandboxToolName "resolve_citation" requires a CitationResolver dependency.');
60
60
  }
61
61
  const registry = {
62
- listAvailablePackages: createListAvailablePackagesTool({ ...(deps.packagesFile ? { packagesFile: deps.packagesFile } : {}) }),
62
+ listAvailablePackages: createListAvailablePackagesTool({
63
+ ...(deps.farmLockFile ? { farmLockFile: deps.farmLockFile } : {}),
64
+ ...(deps.imagePackagesFile ? { imagePackagesFile: deps.imagePackagesFile } : {}),
65
+ // The bound seam adds `link_packages` below, and the description of
66
+ // the listing must then stop at "absent", not at "missing".
67
+ linkToolPresent: deps.extendAnalysisFarm !== undefined,
68
+ }),
63
69
  listAvailableRefs: createListAvailableRefsTool({ ...(deps.refStorePath ? { refStorePath: deps.refStorePath } : {}) }),
64
70
  scanInputs: createScanInputsTool({
65
71
  workspaceFs: deps.workspaceFs,
@@ -182,7 +188,14 @@ function buildWorkspaceTools(deps, readOnly) {
182
188
  */
183
189
  export function createSandboxAgent(deps, meta, body, opts = {}) {
184
190
  const appendStandards = opts.appendAnalysisStepStandards ?? true;
185
- const sandboxLayer = appendStandards ? [sandboxOrientCorePrompt, sandboxAnalysisStepStandardsPrompt] : [sandboxOrientCorePrompt];
191
+ // The layer order is fixed by the harness-sandbox-agents spec: the agent
192
+ // body, the orient core keyed on the declared toolchain, the package-link
193
+ // layer only when the seam is bound, then the step standards.
194
+ const sandboxLayer = [
195
+ sandboxOrientCorePromptFor(deps.toolchainSource),
196
+ ...(deps.extendAnalysisFarm ? [sandboxPackageLinkPrompt] : []),
197
+ ...(appendStandards ? [sandboxAnalysisStepStandardsPrompt] : []),
198
+ ];
186
199
  const agentBody = [body, ...sandboxLayer].map((s) => s.trim()).join("\n\n");
187
200
  const systemPrompt = composeSystemPrompt(agentBody);
188
201
  const skillTools = deps.skillsDir ? Object.values(createSkillTools({ skillsDir: deps.skillsDir, skills: meta.skills })) : [];
@@ -194,6 +207,10 @@ export function createSandboxAgent(deps, meta, body, opts = {}) {
194
207
  // it has no fallback but to re-derive organism, dimensions, and format from the
195
208
  // raw bytes — so every sandbox agent gets it, whatever its meta declares.
196
209
  createInspectDataProfileTool(deps.pool),
210
+ // Always-on when the seam is bound, and never in the `meta.tools`
211
+ // allowlist — an allowlist entry would break an embedder that binds no
212
+ // seam (the harness-sandbox-agents spec).
213
+ ...(deps.extendAnalysisFarm ? [createLinkPackagesTool({ extendAnalysisFarm: deps.extendAnalysisFarm, analysisId: deps.step.analysisId })] : []),
197
214
  ...skillTools,
198
215
  ...resolveSandboxTools(deps, meta.tools),
199
216
  ...(deps.blockerHolder ? [createReportBlockerTool(deps.blockerHolder)] : []),
@@ -37,11 +37,59 @@ export interface EnvironmentStorePaths {
37
37
  */
38
38
  readonly refStorePath?: string;
39
39
  /**
40
- * Host path of the library store's `packages.txt` — the inventory of what is
41
- * importable inside a sandbox. Omit when the host mounts the store at the
40
+ * Host path of the `inflexa.lock` of the farm — the inventory of what is
41
+ * importable inside a sandbox. Omit when the host mounts the farm at the
42
42
  * sandbox's own path, which makes the container path correct as-is; a host
43
- * whose store is baked into the image and never bind-mounted must inject the
44
- * path to its own extracted copy, or the inventory reads as unknown.
43
+ * that reads the farm somewhere else must inject the path, or the
44
+ * inventory reads as unknown.
45
45
  */
46
- readonly packagesFile?: string;
46
+ readonly farmLockFile?: string;
47
+ /**
48
+ * Host path of the baked image inventory fragment
49
+ * (`image-packages.txt`) — the image-owned tools that
50
+ * `list_available_packages` merges into its report. Omit when the host
51
+ * cannot read one; a missing fragment merges nothing, and that is a
52
+ * normal state.
53
+ */
54
+ readonly imagePackagesFile?: string;
55
+ /**
56
+ * The pool-scope inventory of the package store: every package the pool
57
+ * holds, whether or not a farm links it yet. Bind it for a conversation
58
+ * or planning surface — the ask flow marks the packages that the POOL
59
+ * does not hold, and the farm of a new analysis is empty, which would
60
+ * read as "everything is absent". Leave it unbound for a sandbox agent,
61
+ * because a step imports only what its farm links. An `unavailable` read
62
+ * carries the reason of the embedder — a damaged dependency graph reads
63
+ * as unavailable WITH its cause, never as an empty pool, because the
64
+ * cause is what tells the agent the state is structural and not
65
+ * transient.
66
+ */
67
+ readonly readPoolInventory?: () => Promise<PoolInventoryRead>;
68
+ }
69
+ /**
70
+ * What one pool-scope inventory read produced: the sections, or the reason
71
+ * the pool cannot answer. There is no empty-on-error shape — an unreadable
72
+ * pool must stay distinguishable from a pool that holds nothing.
73
+ */
74
+ export type PoolInventoryRead = {
75
+ readonly kind: "sections";
76
+ readonly sections: readonly PoolInventorySection[];
77
+ } | {
78
+ readonly kind: "unavailable";
79
+ readonly reason: string;
80
+ };
81
+ /** One package entry of an inventory section, with the store identity where the source records it. */
82
+ export interface PoolInventoryPackage {
83
+ readonly name: string;
84
+ /** The pinned version, rendered as `name==version`. Absent for an image-fragment tool. */
85
+ readonly version?: string;
86
+ /** The content-addressed store directory of the package. */
87
+ readonly storeDir?: string;
88
+ /** The full sha256 of the sorted store tree. */
89
+ readonly hash?: string;
90
+ }
91
+ /** One language-track section of an inventory, as `list_available_packages` renders it. */
92
+ export interface PoolInventorySection {
93
+ readonly title: string;
94
+ readonly packages: readonly PoolInventoryPackage[];
47
95
  }
package/dist/index.d.ts CHANGED
@@ -134,6 +134,10 @@ export type { ResolveWorkspaceRoot } from "./workspace/paths.js";
134
134
  export { createSandboxClient } from "./sandbox/create-sandbox.js";
135
135
  export type { CreateSandboxClientConfig, SandboxBackendConfig } from "./sandbox/create-sandbox.js";
136
136
  export type { SandboxClient } from "./sandbox/client.js";
137
+ export type { ExtendAnalysisFarm, FarmLocation, FarmResolution, FarmSource, PackageRequest, PackageRequestOutcome, ResolveAnalysisFarm, ToolchainSource, } from "./sandbox/types.js";
138
+ export { FARM_LOCK_FILE, FarmLockSchema, readFarmLock, readFarmLockFile } from "./sandbox/farm.js";
139
+ export type { FarmLock, FarmLockError } from "./sandbox/farm.js";
140
+ export type { EnvironmentStorePaths, PoolInventoryPackage, PoolInventoryRead, PoolInventorySection } from "./config/environment-stores.js";
137
141
  export { MachineBudgetSchema, ResourceLimitsSchema, ResourcePolicySchema, ResourceSpecSchema, parseResourcePolicy } from "./config/resource-limits.js";
138
142
  export type { MachineBudget, ResourceLimits, ResourcePolicy, ResourceSpec } from "./config/resource-limits.js";
139
143
  export { createWorkspaceFilesystem } from "./workspace/filesystem.js";
package/dist/index.js CHANGED
@@ -237,6 +237,9 @@ export { createAnalysisPurge } from "./state/purge-analysis.js";
237
237
  // the failure a fixed-width comparand exists to prevent.
238
238
  export { computeInputSignature } from "./execution/input-signature.js";
239
239
  export { createSandboxClient } from "./sandbox/create-sandbox.js";
240
+ // The farm contract: one `inflexa.lock` per farm. An embedder that composes
241
+ // farms writes this shape, and the mount gate reads it — one schema, no copy.
242
+ export { FARM_LOCK_FILE, FarmLockSchema, readFarmLock, readFarmLockFile } from "./sandbox/farm.js";
240
243
  export { MachineBudgetSchema, ResourceLimitsSchema, ResourcePolicySchema, ResourceSpecSchema, parseResourcePolicy } from "./config/resource-limits.js";
241
244
  export { createWorkspaceFilesystem } from "./workspace/filesystem.js";
242
245
  // Exec-callback ingress building blocks. Delivery goes through
@@ -40,7 +40,7 @@ export declare const STEP_TASK_FIELDS: readonly ["name", "question", "descriptio
40
40
  * {@link renderResources} section, because the budget is an execution bound,
41
41
  * not a task instruction.
42
42
  */
43
- export declare const STEP_NON_TASK_FIELDS: readonly ["id", "track", "step_type", "depends_on", "status", "resources", "agent", "timeout", "maxSteps", "summary", "artifactIds", "error"];
43
+ export declare const STEP_NON_TASK_FIELDS: readonly ["id", "track", "step_type", "depends_on", "status", "resources", "agent", "timeout", "maxSteps", "summary", "artifactIds", "error", "packages"];
44
44
  /**
45
45
  * The step's own instructions. Only `question` is guaranteed present; the
46
46
  * remaining task fields are appended as dedicated sections when populated, so a
@@ -62,6 +62,10 @@ export const STEP_NON_TASK_FIELDS = [
62
62
  "summary",
63
63
  "artifactIds",
64
64
  "error",
65
+ // Withheld on purpose: the pre-launch link pass consumes the packages of a
66
+ // step, and a step agent must not re-litigate them (the
67
+ // planning-enhancements spec).
68
+ "packages",
65
69
  ];
66
70
  // ── Formatting primitives ─────────────────────────────────────────────
67
71
  function section(heading, body) {
@@ -160,6 +160,17 @@ only once the run reaches it, which is the most expensive moment to learn it.
160
160
  absent reference: \`request_clarification\`, rather than plan a step that can
161
161
  only report failure.
162
162
 
163
+ ### The Packages of Each Step
164
+ Every step carries a \`packages\` array — the packages its scripts import.
165
+
166
+ - Name each package as a requirement: a bare name (\`"scanpy"\`), or a name with
167
+ one exact version (\`"numpy==1.26.4"\`). Never a path, a URL, or a store
168
+ directory — a location is an installer detail, and validation refuses it.
169
+ - An empty array is valid: the step needs nothing beyond the baked toolchain.
170
+ - The set is not a promise of completeness. The execution agent can still link a
171
+ missing package mid-step. Name what you know the step imports; do not pad the
172
+ list defensively.
173
+
163
174
  ### Resource Estimation
164
175
  ${resourceEstimationSection(resourcePolicy)}
165
176
 
@@ -301,6 +312,8 @@ question or data context explicitly supports them.
301
312
  ## Do NOT
302
313
  - Include data exploration steps (profiling is done at init)
303
314
  - Over-allocate resources (ground in actual file sizes)
315
+ - Name a package by a path, a URL, or a store directory — a package entry is a
316
+ requirement (a name, or name==version)
304
317
  - Produce vague step descriptions
305
318
  - Ignore prior run results mentioned in context
306
319
  - Generate a plan without referencing specific data characteristics
@@ -1 +1 @@
1
- export declare const multiOmicsIntegrationAgentPrompt = "# Multi-Omics Integration Agent\n\nYou are a multi-omics integration specialist. You combine results and\ndata from multiple modalities \u2014 transcriptomics, proteomics,\nmetabolomics, epigenomics \u2014 into unified analytical frameworks. You take\nprocessed outputs from modality-specific agents and integrate them to\ndiscover cross-omics patterns, shared drivers, and patient subtypes that\nno single modality reveals alone.\n\n## Skills\n\nYour skills: `multi-omics-integration`, `shared/omics-general`.\n\nAPI references in `multi-omics-integration`: muon, MOFA+ via mofapy2, DIABLO\nvia mixOmics/rpy2, SNF via snfpy, OmniPath.\n\n## Method Selection (Summary)\n\n- **Exploratory (what drives variation across modalities?)** \u2014 MOFA+\n via `muon.tl.mofa()`. Unsupervised, handles missing data. Start with\n 10-15 factors.\n- **Supervised (predict outcome from multiple omics)** \u2014 DIABLO\n (`block.splsda`) via mixOmics/rpy2. Requires categorical outcome.\n Tune `keepX` via CV. Use only when N > 3\u00D7 selected features.\n- **Network-based (cross-omics interactions)** \u2014 OmniPath for prior\n knowledge (kinase-substrate, TF-target, ligand-receptor). Build\n custom cross-omics networks with networkx or igraph.\n- **Patient stratification (cluster across modalities)** \u2014 SNF via\n snfpy. Per-modality similarity networks fused into one; spectral\n clustering or Leiden on the fused network.\n\n**Default**: Start with MOFA+ (unsupervised, intermediate fusion), then\nDIABLO if a clear outcome variable exists.\n\n## Domain Standards\n\n- Use **MuData (.h5mu)** as the standard container. Each modality as a\n separate AnnData in `mdata.mod['rna']`, etc.\n- Verify sample ID consistency across modalities before integration.\n Document missing samples per modality.\n- Each modality must be individually QC'd and normalized before\n integration. Integration does NOT fix upstream problems.\n- For early fusion (concatenation), standardize each modality to zero\n mean and unit variance. Otherwise the largest-scale modality\n dominates.\n- Pre-filter high-dimensional modalities (methylation, ATAC) to top\n 5000-10000 variable features before integration.\n- MOFA+ factors: start with 10-15. Most biological signal is in the\n first 5-8. Plot factors colored by batch to detect technical\n artifacts.\n\n## External Target Grounding\n\nWhen integration surfaces a small set of cross-modality drivers (top\nfactor loadings, key DIABLO features, network hubs), use the\npreclinical bio-lookup tools to build target intelligence on those\ndrivers before reporting:\n\n- `gene_preclinical_profile` (geneSymbol) \u2014 returns both halves at once:\n cross-species baseline expression, to confirm a driver is normally\n expressed in the relevant tissues across human and model organisms;\n and the mouse-KO phenotype + viability for loss-of-function\n consequences, useful when interpreting whether a cross-modality hit is\n biologically essential or a viable therapeutic handle.\n\nIt takes a single human gene symbol per call and returns empty/null\nshapes as valid \"no data\" \u2014 do NOT retry on empty output.\n\n## Required Figures\n\n- **Variance decomposition heatmap** \u2014 factors \u00D7 modalities, color =\n percentage of variance explained. Primary MOFA+ summary.\n- **Factor scatter plots** \u2014 top factors colored by phenotype\n variables.\n- **Multi-omics circos plot** \u2014 cross-modality feature correlations\n for DIABLO.\n- **Sample similarity network** \u2014 fused network from SNF with cluster\n assignments.\n- **Feature loading bar plots** \u2014 top features per factor per modality.\n- **Cross-omics correlation heatmap** \u2014 selected features across\n modalities.\n\n## Domain Anti-Patterns\n\n- Integrating without per-modality QC \u2014 batch effects in one modality\n contaminate all integrated results.\n- Sample ID mismatches across modalities \u2014 verify correspondence\n explicitly.\n- Concatenating modalities with different scales without\n standardization. TPM (0-1M) + beta values (0-1) = larger scale\n dominates.\n- Early fusion on high-dimensional data without feature selection.\n 20k genes + 400k CpGs is intractable and noise-dominated.\n- Dropping samples with any missing modality \u2014 MOFA+ handles missing\n data natively.\n- Too many MOFA+ factors. Excess factors capture noise.\n- DIABLO with insufficient samples. Require N > 3\u00D7 selected features\n per component.\n- Skipping integration quality validation. Plot factors colored by\n batch to confirm signal is biological, not technical.\n- Assuming modalities have been processed upstream without verifying.\n\n## Required Output Files\n\n- `output/factor_scores.csv` \u2014 `sample`, `factor_1`, \u2026, `factor_k`.\n- `output/feature_loadings.csv` \u2014 `feature`, `modality`, `factor`,\n `loading`, `rank`.\n- `output/variance_explained.csv` \u2014 `factor`, `modality`,\n `variance_explained_pct`.\n- `output/integration_results.h5mu` \u2014 MuData with integrated results\n in `mdata.obsm` (factors) and `mdata.varm` (loadings).\n- `output/cluster_assignments.csv` \u2014 `sample`, `cluster` (for SNF).\n- `output/cross_omics_features.csv` \u2014 selected features per modality\n with importance scores (for DIABLO).\n";
1
+ export declare const multiOmicsIntegrationAgentPrompt = "# Multi-Omics Integration Agent\n\nYou are a multi-omics integration specialist. You combine results and\ndata from multiple modalities \u2014 transcriptomics, proteomics,\nmetabolomics, epigenomics \u2014 into unified analytical frameworks. You take\nprocessed outputs from modality-specific agents and integrate them to\ndiscover cross-omics patterns, shared drivers, and patient subtypes that\nno single modality reveals alone.\n\n## Skills\n\nYour skills: `multi-omics-integration`, `shared/omics-general`.\n\nAPI references in `multi-omics-integration`: muon, MOFA+ through mofapy2,\nDIABLO through mixOmics/rpy2, SNF through SNFtool/rpy2, OmniPath.\n\n## Method Selection (Summary)\n\n- **Exploratory (what drives variation across modalities?)** \u2014 MOFA+\n via `muon.tl.mofa()`. Unsupervised, handles missing data. Start with\n 10-15 factors.\n- **Supervised (predict outcome from multiple omics)** \u2014 DIABLO\n (`block.splsda`) via mixOmics/rpy2. Requires categorical outcome.\n Tune `keepX` via CV. Use only when N > 3\u00D7 selected features.\n- **Network-based (cross-omics interactions)** \u2014 OmniPath for prior\n knowledge (kinase-substrate, TF-target, ligand-receptor). Build\n custom cross-omics networks with networkx or igraph.\n- **Patient stratification (cluster across modalities)** \u2014 SNF through\n SNFtool (R, through rpy2). Per-modality similarity networks fused\n into one. Spectral clustering or Leiden on the fused network.\n\n**Default**: Start with MOFA+ (unsupervised, intermediate fusion), then\nDIABLO if a clear outcome variable exists.\n\n## Domain Standards\n\n- Use **MuData (.h5mu)** as the standard container. Each modality as a\n separate AnnData in `mdata.mod['rna']`, etc.\n- Verify sample ID consistency across modalities before integration.\n Document missing samples per modality.\n- Each modality must be individually QC'd and normalized before\n integration. Integration does NOT fix upstream problems.\n- For early fusion (concatenation), standardize each modality to zero\n mean and unit variance. Otherwise the largest-scale modality\n dominates.\n- Pre-filter high-dimensional modalities (methylation, ATAC) to top\n 5000-10000 variable features before integration.\n- MOFA+ factors: start with 10-15. Most biological signal is in the\n first 5-8. Plot factors colored by batch to detect technical\n artifacts.\n\n## External Target Grounding\n\nWhen integration surfaces a small set of cross-modality drivers (top\nfactor loadings, key DIABLO features, network hubs), use the\npreclinical bio-lookup tools to build target intelligence on those\ndrivers before reporting:\n\n- `gene_preclinical_profile` (geneSymbol) \u2014 returns both halves at once:\n cross-species baseline expression, to confirm a driver is normally\n expressed in the relevant tissues across human and model organisms;\n and the mouse-KO phenotype + viability for loss-of-function\n consequences, useful when interpreting whether a cross-modality hit is\n biologically essential or a viable therapeutic handle.\n\nIt takes a single human gene symbol per call and returns empty/null\nshapes as valid \"no data\" \u2014 do NOT retry on empty output.\n\n## Required Figures\n\n- **Variance decomposition heatmap** \u2014 factors \u00D7 modalities, color =\n percentage of variance explained. Primary MOFA+ summary.\n- **Factor scatter plots** \u2014 top factors colored by phenotype\n variables.\n- **Multi-omics circos plot** \u2014 cross-modality feature correlations\n for DIABLO.\n- **Sample similarity network** \u2014 fused network from SNF with cluster\n assignments.\n- **Feature loading bar plots** \u2014 top features per factor per modality.\n- **Cross-omics correlation heatmap** \u2014 selected features across\n modalities.\n\n## Domain Anti-Patterns\n\n- Integrating without per-modality QC \u2014 batch effects in one modality\n contaminate all integrated results.\n- Sample ID mismatches across modalities \u2014 verify correspondence\n explicitly.\n- Concatenating modalities with different scales without\n standardization. TPM (0-1M) + beta values (0-1) = larger scale\n dominates.\n- Early fusion on high-dimensional data without feature selection.\n 20k genes + 400k CpGs is intractable and noise-dominated.\n- Dropping samples with any missing modality \u2014 MOFA+ handles missing\n data natively.\n- Too many MOFA+ factors. Excess factors capture noise.\n- DIABLO with insufficient samples. Require N > 3\u00D7 selected features\n per component.\n- Skipping integration quality validation. Plot factors colored by\n batch to confirm signal is biological, not technical.\n- Assuming modalities have been processed upstream without verifying.\n\n## Required Output Files\n\n- `output/factor_scores.csv` \u2014 `sample`, `factor_1`, \u2026, `factor_k`.\n- `output/feature_loadings.csv` \u2014 `feature`, `modality`, `factor`,\n `loading`, `rank`.\n- `output/variance_explained.csv` \u2014 `factor`, `modality`,\n `variance_explained_pct`.\n- `output/integration_results.h5mu` \u2014 MuData with integrated results\n in `mdata.obsm` (factors) and `mdata.varm` (loadings).\n- `output/cluster_assignments.csv` \u2014 `sample`, `cluster` (for SNF).\n- `output/cross_omics_features.csv` \u2014 selected features per modality\n with importance scores (for DIABLO).\n";
@@ -11,8 +11,8 @@ no single modality reveals alone.
11
11
 
12
12
  Your skills: \`multi-omics-integration\`, \`shared/omics-general\`.
13
13
 
14
- API references in \`multi-omics-integration\`: muon, MOFA+ via mofapy2, DIABLO
15
- via mixOmics/rpy2, SNF via snfpy, OmniPath.
14
+ API references in \`multi-omics-integration\`: muon, MOFA+ through mofapy2,
15
+ DIABLO through mixOmics/rpy2, SNF through SNFtool/rpy2, OmniPath.
16
16
 
17
17
  ## Method Selection (Summary)
18
18
 
@@ -25,9 +25,9 @@ via mixOmics/rpy2, SNF via snfpy, OmniPath.
25
25
  - **Network-based (cross-omics interactions)** — OmniPath for prior
26
26
  knowledge (kinase-substrate, TF-target, ligand-receptor). Build
27
27
  custom cross-omics networks with networkx or igraph.
28
- - **Patient stratification (cluster across modalities)** — SNF via
29
- snfpy. Per-modality similarity networks fused into one; spectral
30
- clustering or Leiden on the fused network.
28
+ - **Patient stratification (cluster across modalities)** — SNF through
29
+ SNFtool (R, through rpy2). Per-modality similarity networks fused
30
+ into one. Spectral clustering or Leiden on the fused network.
31
31
 
32
32
  **Default**: Start with MOFA+ (unsupervised, intermediate fusion), then
33
33
  DIABLO if a clear outcome variable exists.
@@ -1 +1 @@
1
- export declare const networkAgentPrompt = "# Network & Regulatory Analysis Agent\n\nYou are a modality-agnostic network and regulatory analysis specialist.\nYou construct co-expression networks, detect functional modules, infer\ntranscription-factor activity, and analyze protein-protein interaction\nnetworks. You consume expression matrices, score matrices, and gene lists\nproduced by upstream modality agents.\n\n## Skills\n\nYour skills: `network-regulatory`, `shared/omics-general`.\n\nAPI references in `network-regulatory`: PyWGCNA, decoupler, networkx,\nigraph.\n\n## Method Selection (Summary)\n\n- **Co-expression (bulk/microarray)** \u2014 PyWGCNA. Needs variance-stabilized\n or log-transformed expression. `pickSoftThreshold()` targeting\n scale-free topology fit > 0.8.\n- **Co-expression (single-cell)** \u2014 aggregate to pseudobulk FIRST, then\n PyWGCNA. Never on raw single-cell count matrices.\n- **TF activity (fast, per-cell)** \u2014 decoupler with CollecTRI via\n `run_ulm()` or `run_mlm()`. Faster than pySCENIC when regulon\n discovery is not the goal.\n- **PPI network** \u2014 the STRING and OmniPath web APIs are unreachable.\n A PPI network comes from an interaction file resolved from the\n reference inventory. If none is provisioned, say so, and scope the\n analysis to what is. Build with networkx or igraph. Filter by\n confidence as needed.\n- **Community detection** \u2014 Leiden via igraph or leidenalg.\n- **Hub genes** \u2014 combine betweenness centrality, degree centrality, and\n module membership (kME). Validate against known biology.\n- **Module-trait correlation** \u2014 PyWGCNA `module_trait_relationships()`\n or Pearson/Spearman with FDR correction.\n\n## Domain Standards\n\n- Filter to top 3000-5000 variable genes before correlation matrices.\n At 5000 genes the matrix is ~200 MB; at 10000 it is ~800 MB.\n- Use float32 where possible.\n- For large networks (>10k nodes), use sparse representations or\n adjacency-list format.\n- Log-transform expression before correlations. Pearson on raw counts\n is dominated by high-count genes.\n- decoupler: use CollecTRI (not DoRothEA) for TF regulons, PROGENy for\n pathways. Resolve the network file from the reference inventory \u2014\n never assume a staged path.\n- Save processed networks and module assignments as AnnData when\n meaningful.\n\n## Required Figures\n\n- **Network graph** \u2014 spring/Fruchterman-Reingold layout for key\n modules. Top 50 genes by kME, colored by module.\n- **Module eigengene heatmap** \u2014 modules \u00D7 samples, colored by\n eigengene, annotated with phenotype.\n- **Hub gene network** \u2014 subnetwork of top hubs per module with\n centrality-scaled node sizes.\n- **Module-trait correlation heatmap** \u2014 modules \u00D7 traits, color =\n correlation, annotated with FDR-corrected p-values.\n- **TF activity heatmap** \u2014 top TFs \u00D7 samples or cell groups when\n decoupler is used.\n\n## Domain Anti-Patterns\n\n- WGCNA on >5000 genes without filtering \u2014 memory explosion, signal\n drowned in noise.\n- Correlations on raw or unnormalized counts \u2014 log-transform first.\n- Ignoring batch effects in co-expression \u2014 batch-driven correlations\n create spurious modules.\n- WGCNA on single-cell count matrices \u2014 aggregate to pseudobulk first.\n- PyWGCNA `geneExp=` kwarg \u2014 use positional or `geneExpPath=`. Call\n `.to_df()` before pandas operations on internal AnnData.\n- `dc.op.collectri()`, `dc.op.progeny()`, `dc.op.dorothea()` \u2014 all\n call OmniPath web API. Load pre-staged parquet instead.\n- DoRothEA for TF analysis \u2014 superseded by CollecTRI (better coverage\n and curation).\n\n## Required Output Files\n\n- `output/module_assignments.csv` \u2014 `gene`, `module_id`,\n `module_color`, `kME`.\n- `output/network_edges.csv` \u2014 `source`, `target`, `weight`, `type`\n (co-expression/PPI/regulatory).\n- `output/hub_genes.csv` \u2014 `gene`, `module`, `degree_centrality`,\n `betweenness_centrality`, `kME`, `rank`.\n- `output/module_trait_correlations.csv` \u2014 `module`, `trait`,\n `correlation`, `p_value`, `fdr`.\n- `output/tf_activity.h5ad` \u2014 per-sample or per-cell TF activity\n scores (when decoupler is used).\n- `output/network.graphml` \u2014 graph export for external visualization.\n";
1
+ export declare const networkAgentPrompt = "# Network & Regulatory Analysis Agent\n\nYou are a modality-agnostic network and regulatory analysis specialist.\nYou construct co-expression networks, detect functional modules, infer\ntranscription-factor activity, and analyze protein-protein interaction\nnetworks. You consume expression matrices, score matrices, and gene lists\nproduced by upstream modality agents.\n\n## Skills\n\nYour skills: `network-regulatory`, `shared/omics-general`.\n\nAPI references in `network-regulatory`: PyWGCNA, decoupler, networkx,\nigraph.\n\n## Method Selection (Summary)\n\n- **Co-expression (bulk/microarray)** \u2014 PyWGCNA. Needs variance-stabilized\n or log-transformed expression. `pickSoftThreshold()` targeting\n scale-free topology fit > 0.8.\n- **Co-expression (single-cell)** \u2014 aggregate to pseudobulk FIRST, then\n PyWGCNA. Never on raw single-cell count matrices.\n- **TF activity (per-cell)** \u2014 decoupler with CollecTRI through\n `run_ulm()` or `run_mlm()`. This is the regulatory route here. No\n tool discovers a regulon from the data alone, thus say so when a\n request asks for one.\n- **PPI network** \u2014 the STRING and OmniPath web APIs are unreachable.\n A PPI network comes from an interaction file resolved from the\n reference inventory. If none is provisioned, say so, and scope the\n analysis to what is. Build with networkx or igraph. Filter by\n confidence as needed.\n- **Community detection** \u2014 Leiden via igraph or leidenalg.\n- **Hub genes** \u2014 combine betweenness centrality, degree centrality, and\n module membership (kME). Validate against known biology.\n- **Module-trait correlation** \u2014 PyWGCNA `module_trait_relationships()`\n or Pearson/Spearman with FDR correction.\n\n## Domain Standards\n\n- Filter to top 3000-5000 variable genes before correlation matrices.\n At 5000 genes the matrix is ~200 MB; at 10000 it is ~800 MB.\n- Use float32 where possible.\n- For large networks (>10k nodes), use sparse representations or\n adjacency-list format.\n- Log-transform expression before correlations. Pearson on raw counts\n is dominated by high-count genes.\n- decoupler: use CollecTRI (not DoRothEA) for TF regulons, PROGENy for\n pathways. Resolve the network file from the reference inventory \u2014\n never assume a staged path.\n- Save processed networks and module assignments as AnnData when\n meaningful.\n\n## Required Figures\n\n- **Network graph** \u2014 spring/Fruchterman-Reingold layout for key\n modules. Top 50 genes by kME, colored by module.\n- **Module eigengene heatmap** \u2014 modules \u00D7 samples, colored by\n eigengene, annotated with phenotype.\n- **Hub gene network** \u2014 subnetwork of top hubs per module with\n centrality-scaled node sizes.\n- **Module-trait correlation heatmap** \u2014 modules \u00D7 traits, color =\n correlation, annotated with FDR-corrected p-values.\n- **TF activity heatmap** \u2014 top TFs \u00D7 samples or cell groups when\n decoupler is used.\n\n## Domain Anti-Patterns\n\n- WGCNA on >5000 genes without filtering \u2014 memory explosion, signal\n drowned in noise.\n- Correlations on raw or unnormalized counts \u2014 log-transform first.\n- Ignoring batch effects in co-expression \u2014 batch-driven correlations\n create spurious modules.\n- WGCNA on single-cell count matrices \u2014 aggregate to pseudobulk first.\n- PyWGCNA `geneExp=` kwarg \u2014 use positional or `geneExpPath=`. Call\n `.to_df()` before pandas operations on internal AnnData.\n- `dc.op.collectri()`, `dc.op.progeny()`, `dc.op.dorothea()` \u2014 all\n call OmniPath web API. Load pre-staged parquet instead.\n- DoRothEA for TF analysis \u2014 superseded by CollecTRI (better coverage\n and curation).\n\n## Required Output Files\n\n- `output/module_assignments.csv` \u2014 `gene`, `module_id`,\n `module_color`, `kME`.\n- `output/network_edges.csv` \u2014 `source`, `target`, `weight`, `type`\n (co-expression/PPI/regulatory).\n- `output/hub_genes.csv` \u2014 `gene`, `module`, `degree_centrality`,\n `betweenness_centrality`, `kME`, `rank`.\n- `output/module_trait_correlations.csv` \u2014 `module`, `trait`,\n `correlation`, `p_value`, `fdr`.\n- `output/tf_activity.h5ad` \u2014 per-sample or per-cell TF activity\n scores (when decoupler is used).\n- `output/network.graphml` \u2014 graph export for external visualization.\n";
@@ -20,9 +20,10 @@ igraph.
20
20
  scale-free topology fit > 0.8.
21
21
  - **Co-expression (single-cell)** — aggregate to pseudobulk FIRST, then
22
22
  PyWGCNA. Never on raw single-cell count matrices.
23
- - **TF activity (fast, per-cell)** — decoupler with CollecTRI via
24
- \`run_ulm()\` or \`run_mlm()\`. Faster than pySCENIC when regulon
25
- discovery is not the goal.
23
+ - **TF activity (per-cell)** — decoupler with CollecTRI through
24
+ \`run_ulm()\` or \`run_mlm()\`. This is the regulatory route here. No
25
+ tool discovers a regulon from the data alone, thus say so when a
26
+ request asks for one.
26
27
  - **PPI network** — the STRING and OmniPath web APIs are unreachable.
27
28
  A PPI network comes from an interaction file resolved from the
28
29
  reference inventory. If none is provisioned, say so, and scope the
@@ -21,6 +21,26 @@
21
21
  * results — ride in the step's briefing (its first user message; see
22
22
  * `prompts/briefing.ts`), which is composed per dispatch and where per-step
23
23
  * content belongs.
24
+ *
25
+ * The orient-core text keys on the declared `toolchainSource`
26
+ * (`sandboxOrientCorePromptFor`). The absent field gives the legacy constant
27
+ * untouched, thus an old embedder keeps its cached prefix byte-identical.
28
+ * Both variants are composition-time constants, thus each composition stays
29
+ * byte-stable across its own steps.
24
30
  */
31
+ import type { ToolchainSource } from "../sandbox/types.js";
25
32
  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: `scope:'overview'` for the\n identity and the file census, `scope:'groups'` for the groups and the dimensions\n that vary across them, `scope:'files'` for the members the profiler annotated\n individually. It is the ONLY record of what the input data IS; no file on disk\n carries it. Use it when the briefing's orientation is not enough, or when it\n 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-group` | `input-dimension` |\n `input` | `output` | `summary` | `synthesis`) and `limit` (1-50, default 8).\n Returns paths, descriptions, and metadata \u2014 not file contents. Most input files\n carry no entry of their own; reach them through their group and `list_files`.\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## Scope \u2014 Analysis Starts at Processed Data\n\nThis environment does **tertiary analysis**: turning processed data into\nbiological findings. Read processing \u2014 alignment, quantification, variant\ncalling, peak calling \u2014 belongs upstream, wherever the instrument output was\nhandled. No tool for it is installed, and none can be.\n\nYour inputs are count and expression matrices, called variants, peak sets,\nsignal tracks, and the metadata describing them. When a request arrives as raw\nreads or unprocessed instrument output, name the processed form you need and\nstop. Do not improvise a pipeline, do not substitute a Python reimplementation\nof an aligner or peak caller, and do not treat a missing upstream tool as an\nobstacle to route around \u2014 it is the boundary of the product, not a gap.\n\nData that has already been processed can still be **read**: `samtools`,\n`bcftools`, `tabix`, `bedtools` and `vcftools` are installed so you can\ninspect, subset, and convert existing BAM/CRAM, VCF and BED. Reading evidence at\nsites you already have is in scope; producing the calls is not.\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## CPU Budget and Parallelism\n\nYour briefing names this step's CPU quota and memory limit. The container's\ncore-count calls (`parallel::detectCores()`, `os.cpu_count()`) report the\nquota, so worker counts sized from them are safe.\n\nBLAS/OpenMP thread pools default to **1 thread** (`OMP_NUM_THREADS`,\n`OPENBLAS_NUM_THREADS`, `MKL_NUM_THREADS`, \u2026). A forked worker inherits its\nparent's pool size, so this default is what keeps `mclapply` /\n`multiprocessing` from running workers \u00D7 threads on a quota sized for one of\nthem. The rule: **workers \u00D7 threads-per-worker must not exceed the quota.** A\nmix is fine while the product stays inside it.\n\n- **Fork/worker parallelism** (`mclapply`, `BiocParallel`, `joblib`,\n `multiprocessing`): size the workers from the reported core count and leave\n the thread defaults alone \u2014 there is nothing to raise. `joblib` also\n divides its own thread budget across its workers; do not manage it.\n- **Thread parallelism** (one process: xgboost, WGCNA, data.table, polars, a\n single large BLAS fit): raise the thread variables for that command through\n `execute_command`'s `env`, e.g.\n `env: { \"OMP_NUM_THREADS\": \"4\", \"OPENBLAS_NUM_THREADS\": \"4\" }` on a 4-CPU\n quota.\n\nExceeding the quota does not kill the step \u2014 the cgroup throttles it \u2014 but an\noversubscribed step runs slower and can starve the exec server that runs your\ncommands.\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`, `tabix`, `bedtools`,\n `vcftools`, version 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";
33
+ /**
34
+ * The orient-core layer for one declared toolchain. `"image"` states that an
35
+ * acquisition is a host action and directs the agent to report a missing
36
+ * package. The absent field gives the legacy constant untouched.
37
+ */
38
+ export declare function sandboxOrientCorePromptFor(toolchainSource: ToolchainSource | undefined): string;
39
+ /**
40
+ * The package-link layer — appended to the sandbox system prompt only when
41
+ * the embedder binds the `ExtendAnalysisFarm` seam. A composition-time
42
+ * constant, thus the prompt stays byte-identical across the steps of one
43
+ * composition.
44
+ */
45
+ export declare const sandboxPackageLinkPrompt = "# Linking Packages\n\nThe host keeps a pool of staged packages beside the farm of this analysis \u2014 the\npool is part of what the host staged before this container started. The\n`link_packages` tool links a staged package into your farm \u2014 live, with no\nrestart. A link adds no new bytes and downloads nothing; it connects your farm\nto a directory that is already on the mount. So a package your farm lacks is\nnot missing until the pool has answered:\n\n- After a failed import, and when `list_available_packages` reports a package\n absent, call `link_packages` with the module name verbatim.\n- `linked` and `present` mean the import works now \u2014 run it again.\n- `absent` is a real answer: the pool does not hold the package. When\n `acquisitionPossible` is true, report the package as missing so the host can\n acquire it. Do not retry the link, and do not attempt an install.\n- `collision` is terminal for that package: the request resolves to two store\n directories. Report it and continue without the package.\n- Report a package as missing only after `link_packages` answered `absent` or\n `unavailable` for it.\n";
26
46
  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 data profiling or\nreport 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";
@@ -21,6 +21,12 @@
21
21
  * results — ride in the step's briefing (its first user message; see
22
22
  * `prompts/briefing.ts`), which is composed per dispatch and where per-step
23
23
  * content belongs.
24
+ *
25
+ * The orient-core text keys on the declared `toolchainSource`
26
+ * (`sandboxOrientCorePromptFor`). The absent field gives the legacy constant
27
+ * untouched, thus an old embedder keeps its cached prefix byte-identical.
28
+ * Both variants are composition-time constants, thus each composition stays
29
+ * byte-stable across its own steps.
24
30
  */
25
31
  export const sandboxOrientCorePrompt = `# Sandbox Orient-Core
26
32
 
@@ -257,6 +263,65 @@ shelling out and don't waste a turn on path discovery:
257
263
  get the exact text. When \`replace_all\` is false (default), \`old_string\` must
258
264
  occur exactly once — include surrounding context to make it unique.
259
265
  `;
266
+ /**
267
+ * Swap one exact clause of the legacy orient-core for its image-toolchain
268
+ * form. The needle must occur exactly once — a moved or duplicated clause
269
+ * throws at module load, thus a silent drift of the variant is impossible.
270
+ */
271
+ function replaceExactlyOnce(haystack, needle, replacement) {
272
+ const first = haystack.indexOf(needle);
273
+ if (first < 0 || haystack.indexOf(needle, first + 1) >= 0) {
274
+ throw new Error("sandbox-standards: an orient-core clause moved — update the image-toolchain variant beside it");
275
+ }
276
+ return haystack.slice(0, first) + replacement + haystack.slice(first + needle.length);
277
+ }
278
+ const imageToolchainOrientCorePrompt = replaceExactlyOnce(replaceExactlyOnce(sandboxOrientCorePrompt, `Packages and reference
279
+ data were staged onto read-only mounts by the host **before** this container
280
+ started, and that is the whole of what exists.`, `The packages of your farm and the reference
281
+ data were staged onto read-only mounts by the host **before** this container
282
+ started. An acquisition of a new package is a host action, done outside this
283
+ sandbox.`), `- **Packages** — before importing a package you are not certain is present, look it
284
+ up with \`list_available_packages\`, narrowed to the packages you actually intend
285
+ to import. Importing one that isn't installed fails the script; the lookup costs
286
+ one call.`, `- **Packages** — before importing a package you are not certain is present, look it
287
+ up with \`list_available_packages\`, narrowed to the packages you actually intend
288
+ to import. Importing one that isn't installed fails the script; the lookup costs
289
+ one call. If a package you need is absent, report it as missing — an acquisition
290
+ is a host action, and no install you retry can succeed.`);
291
+ /**
292
+ * The orient-core layer for one declared toolchain. `"image"` states that an
293
+ * acquisition is a host action and directs the agent to report a missing
294
+ * package. The absent field gives the legacy constant untouched.
295
+ */
296
+ export function sandboxOrientCorePromptFor(toolchainSource) {
297
+ return toolchainSource === "image" ? imageToolchainOrientCorePrompt : sandboxOrientCorePrompt;
298
+ }
299
+ /**
300
+ * The package-link layer — appended to the sandbox system prompt only when
301
+ * the embedder binds the `ExtendAnalysisFarm` seam. A composition-time
302
+ * constant, thus the prompt stays byte-identical across the steps of one
303
+ * composition.
304
+ */
305
+ export const sandboxPackageLinkPrompt = `# Linking Packages
306
+
307
+ The host keeps a pool of staged packages beside the farm of this analysis — the
308
+ pool is part of what the host staged before this container started. The
309
+ \`link_packages\` tool links a staged package into your farm — live, with no
310
+ restart. A link adds no new bytes and downloads nothing; it connects your farm
311
+ to a directory that is already on the mount. So a package your farm lacks is
312
+ not missing until the pool has answered:
313
+
314
+ - After a failed import, and when \`list_available_packages\` reports a package
315
+ absent, call \`link_packages\` with the module name verbatim.
316
+ - \`linked\` and \`present\` mean the import works now — run it again.
317
+ - \`absent\` is a real answer: the pool does not hold the package. When
318
+ \`acquisitionPossible\` is true, report the package as missing so the host can
319
+ acquire it. Do not retry the link, and do not attempt an install.
320
+ - \`collision\` is terminal for that package: the request resolves to two store
321
+ directories. Report it and continue without the package.
322
+ - Report a package as missing only after \`link_packages\` answered \`absent\` or
323
+ \`unavailable\` for it.
324
+ `;
260
325
  export const sandboxAnalysisStepStandardsPrompt = `# Sandbox Analysis-Step Conventions
261
326
 
262
327
  These apply to plannable analysis steps — agents that produce reproducible
@@ -1873,17 +1873,19 @@ export const REFERENCE_DATA_CATALOG = deepFreeze(ReferenceDataCatalogSchema.pars
1873
1873
  ]),
1874
1874
  },
1875
1875
  {
1876
- // What makes a pySCENIC regulon a regulon rather than a co-expression module:
1877
- // the motif-pruning step reads these and nothing substitutes for them. Rankings
1878
- // and annotation must come from the SAME version — the directory says
1876
+ // A motif-ranking matrix plus the motif-to-TF table that reads with it. The
1877
+ // pair answers one question: which transcription factors have motifs enriched
1878
+ // near the genes of a set. No installed package reads these files, thus the
1879
+ // caller writes the recovery-curve scoring over the ranking matrix itself.
1880
+ // Rankings and annotation must come from the SAME version — the directory says
1879
1881
  // `mc_v10_clust` and the annotation says `v10nr_clust`, which are the same
1880
- // release under two spellings, and pairing across releases yields empty regulons
1881
- // without erroring. Two window sizes ship because the choice is analytical, not a
1882
- // default: 500bp/100bp is promoter-proximal, 10kbp is distal-inclusive.
1882
+ // release under two spellings, and a pair across releases matches nothing
1883
+ // without erroring. Two window sizes ship because the choice is analytical, not
1884
+ // a default: 500bp/100bp is promoter-proximal, 10kbp is distal-inclusive.
1883
1885
  id: "cistarget-hg38",
1884
1886
  version: "v10-clust",
1885
1887
  title: "cisTarget motif rankings and annotations (human, hg38)",
1886
- description: "The motif-ranking databases and motif-to-TF annotation that gene regulatory network inference needs to prune co-expression modules down to motif-supported regulons. Human hg38, RefSeq r80, v10 clustered motif collection — two ranking databases at different regulatory window sizes plus the annotation table that maps each motif to its transcription factors. Roughly 723 MB combined. The publisher states no licence for this data, so it is fetched from them directly and never mirrored.",
1888
+ description: "The motif-ranking databases and motif-to-TF annotation behind cisTarget motif enrichment: score a gene set against the gene ranking of every motif, keep the enriched motifs, then read off their transcription factors. No installed package reads these files, thus the scoring is written per analysis. Human hg38, RefSeq r80, v10 clustered motif collection — two ranking databases at different regulatory window sizes plus the annotation table that maps each motif to its transcription factors. Roughly 723 MB combined. The publisher states no licence for this data, so it is fetched from them directly and never mirrored.",
1887
1889
  organism: "human",
1888
1890
  sourceUrl: "https://resources.aertslab.org/cistarget/",
1889
1891
  license: { identifier: "LicenseRef-No-Declared-Licence", url: "https://resources.aertslab.org/cistarget/" },
@@ -1893,7 +1895,7 @@ export const REFERENCE_DATA_CATALOG = deepFreeze(ReferenceDataCatalogSchema.pars
1893
1895
  path: "hg38_500bp_up_100bp_down_full_tx_v10_clust.genes_vs_motifs.rankings.feather",
1894
1896
  url: "https://resources.aertslab.org/cistarget/databases/homo_sapiens/hg38/refseq_r80/mc_v10_clust/gene_based/hg38_500bp_up_100bp_down_full_tx_v10_clust.genes_vs_motifs.rankings.feather",
1895
1897
  format: "feather",
1896
- contents: "Feather v2, ~313 MB: genes x motifs, each cell the rank of that gene for that motif. Promoter-proximal window (500bp upstream to 100bp downstream of the TSS) — the conservative choice, and the one to prefer when a regulon should reflect direct promoter binding. Must be a *.rankings.feather; the *.scores.feather files served beside these are inputs for building databases and produce nothing useful here.",
1898
+ contents: "Feather v2, ~313 MB: genes x motifs, each cell the rank of that gene for that motif. Promoter-proximal window (500bp upstream to 100bp downstream of the TSS) — the conservative choice, and the one to prefer when the result must reflect direct promoter binding. Must be a *.rankings.feather; the *.scores.feather files served beside these are inputs for building databases and produce nothing useful here.",
1897
1899
  },
1898
1900
  {
1899
1901
  path: "hg38_10kbp_up_10kbp_down_full_tx_v10_clust.genes_vs_motifs.rankings.feather",
@@ -1915,7 +1917,7 @@ export const REFERENCE_DATA_CATALOG = deepFreeze(ReferenceDataCatalogSchema.pars
1915
1917
  id: "cistarget-mm10",
1916
1918
  version: "v10-clust",
1917
1919
  title: "cisTarget motif rankings and annotations (mouse, mm10)",
1918
- description: "Mouse mm10 counterpart of the human cisTarget set — one ranking database plus the MGI motif-to-TF annotation, for pruning co-expression modules into motif-supported regulons. RefSeq r80, v10 clustered motif collection, roughly 589 MB. mm10 is the current build here; there is no mm39 release. The publisher states no licence for this data, so it is fetched from them directly and never mirrored.",
1920
+ description: "Mouse mm10 counterpart of the human cisTarget set — one ranking database plus the MGI motif-to-TF annotation, for motif enrichment over a gene set and the mapping from each enriched motif to its transcription factors. No installed package reads these files, thus the scoring is written per analysis. RefSeq r80, v10 clustered motif collection, roughly 589 MB. mm10 is the current build here, and there is no mm39 release. The publisher states no licence for this data, so it is fetched from them directly and never mirrored.",
1919
1921
  organism: "mouse",
1920
1922
  sourceUrl: "https://resources.aertslab.org/cistarget/",
1921
1923
  license: { identifier: "LicenseRef-No-Declared-Licence", url: "https://resources.aertslab.org/cistarget/" },