@botbotgo/agent-harness 0.0.133 → 0.0.135
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/README.zh.md +47 -0
- package/dist/contracts/workspace.d.ts +10 -0
- package/dist/extensions.js +8 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/resource/mcp-tool-support.d.ts +4 -0
- package/dist/resource/mcp-tool-support.js +112 -35
- package/dist/resource/resource-impl.js +198 -6
- package/dist/runtime/adapter/runtime-shell.d.ts +3 -1
- package/dist/runtime/adapter/runtime-shell.js +2 -1
- package/dist/runtime/adapter/tool/tool-arguments.js +1 -0
- package/dist/runtime/adapter/tool/tool-hitl.js +3 -0
- package/dist/runtime/agent-runtime-adapter.d.ts +6 -0
- package/dist/runtime/agent-runtime-adapter.js +32 -2
- package/dist/runtime/harness.js +2 -0
- package/dist/workspace/agent-binding-compiler.d.ts +2 -2
- package/dist/workspace/agent-binding-compiler.js +78 -6
- package/dist/workspace/compile.js +16 -0
- package/dist/workspace/object-loader.js +100 -3
- package/dist/workspace/resource-compilers.js +6 -0
- package/dist/workspace/tool-hydration.js +87 -13
- package/dist/workspace/yaml-object-reader.js +39 -12
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ import { resolveIsolatedResourceModulePath } from "../resource/isolation.js";
|
|
|
6
6
|
import { isExternalSourceLocator, resolveResourcePackageRoot } from "../resource/sources.js";
|
|
7
7
|
import { discoverToolModuleDefinitions, isSupportedToolModulePath } from "../tool-modules.js";
|
|
8
8
|
import { fileExists } from "../utils/fs.js";
|
|
9
|
+
import { parseToolObject } from "./resource-compilers.js";
|
|
9
10
|
import { readNamedModelItems, readNamedYamlItems, readYamlItems, } from "./yaml-object-reader.js";
|
|
10
11
|
export { normalizeYamlItem, readYamlItems } from "./yaml-object-reader.js";
|
|
11
12
|
const CONVENTIONAL_OBJECT_DIRECTORIES = ["tools"];
|
|
@@ -93,6 +94,89 @@ function readRefArray(items) {
|
|
|
93
94
|
: undefined)
|
|
94
95
|
.filter((item) => Boolean(item));
|
|
95
96
|
}
|
|
97
|
+
function readToolBindingArray(items) {
|
|
98
|
+
return toArray(items)
|
|
99
|
+
.map((item) => {
|
|
100
|
+
if (typeof item === "string") {
|
|
101
|
+
return { ref: item };
|
|
102
|
+
}
|
|
103
|
+
if (typeof item !== "object" || !item || !("ref" in item) || typeof item.ref !== "string") {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
const { ref, ...rest } = item;
|
|
107
|
+
const overrides = Object.keys(rest).length > 0
|
|
108
|
+
? cloneConfigValue(rest)
|
|
109
|
+
: undefined;
|
|
110
|
+
return {
|
|
111
|
+
ref,
|
|
112
|
+
...(overrides ? { overrides } : {}),
|
|
113
|
+
};
|
|
114
|
+
})
|
|
115
|
+
.filter((item) => Boolean(item));
|
|
116
|
+
}
|
|
117
|
+
function readInlineToolObjects(items, sourcePath, agentId) {
|
|
118
|
+
const sourceDir = path.dirname(sourcePath);
|
|
119
|
+
const bindings = [];
|
|
120
|
+
const inlineTools = [];
|
|
121
|
+
toArray(items).forEach((item, index) => {
|
|
122
|
+
if (typeof item === "string") {
|
|
123
|
+
bindings.push({ ref: item });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (typeof item !== "object" || !item) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if ("ref" in item && typeof item.ref === "string") {
|
|
130
|
+
const { ref, ...rest } = item;
|
|
131
|
+
const overrides = Object.keys(rest).length > 0
|
|
132
|
+
? cloneConfigValue(rest)
|
|
133
|
+
: undefined;
|
|
134
|
+
bindings.push({ ref, ...(overrides ? { overrides } : {}) });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const raw = cloneConfigValue(item);
|
|
138
|
+
const implementation = asObject(raw.implementation);
|
|
139
|
+
const name = typeof raw.name === "string" && raw.name.trim().length > 0
|
|
140
|
+
? raw.name.trim()
|
|
141
|
+
: typeof raw.id === "string" && raw.id.trim().length > 0
|
|
142
|
+
? raw.id.trim()
|
|
143
|
+
: `tool-${index + 1}`;
|
|
144
|
+
const syntheticId = `${agentId}__${name}`;
|
|
145
|
+
const implementationPath = typeof implementation?.path === "string"
|
|
146
|
+
? resolveModuleRelativePath(path.resolve(sourceDir, implementation.path), undefined)
|
|
147
|
+
: undefined;
|
|
148
|
+
const inferredType = typeof raw.type === "string"
|
|
149
|
+
? raw.type
|
|
150
|
+
: raw.refs !== undefined || raw.bundle !== undefined
|
|
151
|
+
? "bundle"
|
|
152
|
+
: raw.providerTool !== undefined || raw.provider !== undefined
|
|
153
|
+
? "provider"
|
|
154
|
+
: raw.backend !== undefined || raw.operation !== undefined
|
|
155
|
+
? "backend"
|
|
156
|
+
: raw.mcp !== undefined
|
|
157
|
+
? "mcp"
|
|
158
|
+
: "function";
|
|
159
|
+
if (inferredType === "function" && !implementationPath) {
|
|
160
|
+
throw new Error(`Inline tool ${name} must define implementation.path`);
|
|
161
|
+
}
|
|
162
|
+
const workspaceObject = {
|
|
163
|
+
id: syntheticId,
|
|
164
|
+
kind: "tool",
|
|
165
|
+
sourcePath: implementationPath ?? sourcePath,
|
|
166
|
+
value: {
|
|
167
|
+
id: syntheticId,
|
|
168
|
+
...raw,
|
|
169
|
+
...(typeof implementation?.export === "string" && typeof raw.implementationName !== "string"
|
|
170
|
+
? { implementationName: implementation.export }
|
|
171
|
+
: {}),
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
const parsedTool = parseToolObject(workspaceObject);
|
|
175
|
+
inlineTools.push(parsedTool);
|
|
176
|
+
bindings.push({ ref: `tool/${syntheticId}` });
|
|
177
|
+
});
|
|
178
|
+
return { bindings, inlineTools };
|
|
179
|
+
}
|
|
96
180
|
function readPathArray(items) {
|
|
97
181
|
return toArray(items)
|
|
98
182
|
.map((item) => typeof item === "string"
|
|
@@ -347,6 +431,7 @@ function readAgentConfig(item, options = {}) {
|
|
|
347
431
|
export function parseAgentItem(item, sourcePath) {
|
|
348
432
|
const moduleRoot = moduleRootForSourcePath(sourcePath, "agents");
|
|
349
433
|
const subagentRefs = readExecutionValue(item, "subagents", readRefArray);
|
|
434
|
+
const { bindings: toolBindings, inlineTools } = readInlineToolObjects(readExecutionValue(item, "tools", toArray), sourcePath, String(item.id));
|
|
350
435
|
const subagentPathRefs = readExecutionValue(item, "subagents", readPathArray).map((entry) => resolveModuleRelativePath(entry, moduleRoot));
|
|
351
436
|
const executionMode = String(resolveExecutionBackend(item) ?? "deepagent");
|
|
352
437
|
const runtime = readRuntimeConfig(item);
|
|
@@ -360,7 +445,9 @@ export function parseAgentItem(item, sourcePath) {
|
|
|
360
445
|
description: String(item.description ?? ""),
|
|
361
446
|
modelRef: readExecutionValue(item, "modelRef", readSingleRef) ?? "",
|
|
362
447
|
runRoot: typeof runtime?.runRoot === "string" ? runtime.runRoot : undefined,
|
|
363
|
-
toolRefs:
|
|
448
|
+
toolRefs: toolBindings.map((binding) => binding.ref),
|
|
449
|
+
toolBindings,
|
|
450
|
+
inlineTools,
|
|
364
451
|
mcpServers: readExecutionValue(item, "mcpServers", readObjectArray),
|
|
365
452
|
skillPathRefs: readExecutionValue(item, "skills", readPathArray).map((entry) => resolveModuleRelativePath(entry, moduleRoot)),
|
|
366
453
|
memorySources: readExecutionValue(item, "memory", readPathArray).map((entry) => resolveModuleRelativePath(entry, moduleRoot)),
|
|
@@ -409,9 +496,19 @@ function mergeValues(base, override) {
|
|
|
409
496
|
}
|
|
410
497
|
function mergeRawItemRecord(records, key, item, sourcePath) {
|
|
411
498
|
const current = records.get(key);
|
|
499
|
+
const mergedItem = current ? mergeValues(current.item, item) : item;
|
|
500
|
+
const kind = typeof mergedItem.kind === "string" ? mergedItem.kind : undefined;
|
|
501
|
+
const type = typeof mergedItem.type === "string" ? mergedItem.type : undefined;
|
|
502
|
+
const resolvedSourcePath = current &&
|
|
503
|
+
kind === "tool" &&
|
|
504
|
+
type === "function" &&
|
|
505
|
+
isSupportedToolModulePath(current.sourcePath) &&
|
|
506
|
+
!isSupportedToolModulePath(sourcePath)
|
|
507
|
+
? current.sourcePath
|
|
508
|
+
: sourcePath;
|
|
412
509
|
const mergedRecord = {
|
|
413
|
-
item:
|
|
414
|
-
sourcePath,
|
|
510
|
+
item: mergedItem,
|
|
511
|
+
sourcePath: resolvedSourcePath,
|
|
415
512
|
};
|
|
416
513
|
records.set(key, mergedRecord);
|
|
417
514
|
return mergedRecord;
|
|
@@ -248,7 +248,13 @@ export function parseToolObject(object) {
|
|
|
248
248
|
...(mcpServerConfig && Object.keys(mcpServerConfig).length > 0 ? { mcpServer: mcpServerConfig } : {}),
|
|
249
249
|
}
|
|
250
250
|
: undefined),
|
|
251
|
+
subprocess: value.subprocess === true,
|
|
251
252
|
inputSchemaRef: typeof asObject(value.inputSchema)?.ref === "string" ? String(asObject(value.inputSchema)?.ref) : undefined,
|
|
253
|
+
embeddingModelRef: typeof value.embeddingModelRef === "string"
|
|
254
|
+
? value.embeddingModelRef
|
|
255
|
+
: typeof asObject(value.embeddingModel)?.ref === "string"
|
|
256
|
+
? String(asObject(value.embeddingModel)?.ref)
|
|
257
|
+
: undefined,
|
|
252
258
|
backendOperation: typeof backend?.operation === "string"
|
|
253
259
|
? backend.operation
|
|
254
260
|
: typeof value.operation === "string"
|
|
@@ -20,6 +20,18 @@ function toMcpServerConfig(server) {
|
|
|
20
20
|
function readStringArray(value) {
|
|
21
21
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim().length > 0) : [];
|
|
22
22
|
}
|
|
23
|
+
function readStringRecord(value) {
|
|
24
|
+
if (typeof value !== "object" || !value || Array.isArray(value)) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
|
|
28
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
29
|
+
}
|
|
30
|
+
function asObject(value) {
|
|
31
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
32
|
+
? value
|
|
33
|
+
: undefined;
|
|
34
|
+
}
|
|
23
35
|
function compileRegexList(value, label) {
|
|
24
36
|
return readStringArray(value).map((pattern) => {
|
|
25
37
|
try {
|
|
@@ -53,6 +65,52 @@ function shouldIncludeRemoteMcpTool(filter, toolName) {
|
|
|
53
65
|
}
|
|
54
66
|
return true;
|
|
55
67
|
}
|
|
68
|
+
function normalizeAgentMcpServerUsage(item) {
|
|
69
|
+
const config = asObject(item.config);
|
|
70
|
+
if (!config) {
|
|
71
|
+
return item;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
...config,
|
|
75
|
+
...item,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function hasMcpServerUsageOverrides(item) {
|
|
79
|
+
return [
|
|
80
|
+
"transport",
|
|
81
|
+
"command",
|
|
82
|
+
"args",
|
|
83
|
+
"env",
|
|
84
|
+
"cwd",
|
|
85
|
+
"url",
|
|
86
|
+
"token",
|
|
87
|
+
"headers",
|
|
88
|
+
].some((key) => item[key] !== undefined);
|
|
89
|
+
}
|
|
90
|
+
function mergeReferencedMcpServer(referencedServer, item, agentId, name, sourcePath) {
|
|
91
|
+
return {
|
|
92
|
+
id: `${agentId}.${name}`,
|
|
93
|
+
transport: item.transport === "stdio" || item.transport === "http" || item.transport === "sse" || item.transport === "websocket"
|
|
94
|
+
? item.transport
|
|
95
|
+
: referencedServer.transport,
|
|
96
|
+
url: typeof item.url === "string" ? item.url : referencedServer.url,
|
|
97
|
+
command: typeof item.command === "string" ? item.command : referencedServer.command,
|
|
98
|
+
args: Array.isArray(item.args)
|
|
99
|
+
? item.args.filter((entry) => typeof entry === "string")
|
|
100
|
+
: referencedServer.args,
|
|
101
|
+
env: {
|
|
102
|
+
...(referencedServer.env ?? {}),
|
|
103
|
+
...(readStringRecord(item.env) ?? {}),
|
|
104
|
+
},
|
|
105
|
+
cwd: typeof item.cwd === "string" ? item.cwd : referencedServer.cwd,
|
|
106
|
+
token: typeof item.token === "string" ? item.token : referencedServer.token,
|
|
107
|
+
headers: {
|
|
108
|
+
...(referencedServer.headers ?? {}),
|
|
109
|
+
...(readStringRecord(item.headers) ?? {}),
|
|
110
|
+
},
|
|
111
|
+
sourcePath,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
56
114
|
async function hydrateExternalToolSource(tools, source, workspaceRoot) {
|
|
57
115
|
const externalRoot = await ensureExternalResourceSource(source, workspaceRoot);
|
|
58
116
|
const discoveredToolRefs = [];
|
|
@@ -102,35 +160,51 @@ export async function hydrateResourceAndExternalTools(tools, toolSourceRefs, wor
|
|
|
102
160
|
name: existing?.name || resourceTool.name,
|
|
103
161
|
description: existing?.description || resourceTool.description,
|
|
104
162
|
config: existing?.config,
|
|
163
|
+
embeddingModelRef: existing?.embeddingModelRef,
|
|
105
164
|
backendOperation: existing?.backendOperation ?? resourceTool.backendOperation,
|
|
106
165
|
bundleRefs: existing?.bundleRefs ?? [],
|
|
107
166
|
hitl: existing?.hitl ?? resourceTool.hitl,
|
|
108
167
|
retryable: existing?.retryable ?? resourceTool.retryable,
|
|
109
|
-
sourcePath:
|
|
168
|
+
sourcePath: resourceTool.toolPath,
|
|
110
169
|
});
|
|
111
170
|
}
|
|
112
171
|
}
|
|
113
172
|
export async function hydrateAgentMcpTools(agents, mcpServers, tools) {
|
|
114
173
|
for (const agent of agents) {
|
|
115
174
|
const discoveredRefs = new Set(agent.toolRefs);
|
|
116
|
-
for (const
|
|
175
|
+
for (const rawItem of agent.mcpServers ?? []) {
|
|
176
|
+
const item = normalizeAgentMcpServerUsage(rawItem);
|
|
177
|
+
const serverRef = typeof item.ref === "string" ? item.ref.trim() : "";
|
|
178
|
+
const referencedServerId = serverRef.startsWith("mcp/") ? serverRef.slice(4) : serverRef;
|
|
179
|
+
const referencedServer = referencedServerId ? mcpServers.get(referencedServerId) : undefined;
|
|
117
180
|
const name = typeof item.name === "string" && item.name.trim()
|
|
118
181
|
? item.name.trim()
|
|
119
|
-
:
|
|
120
|
-
?
|
|
121
|
-
: ""
|
|
182
|
+
: referencedServer?.id
|
|
183
|
+
? referencedServer.id
|
|
184
|
+
: typeof item.id === "string" && item.id.trim()
|
|
185
|
+
? item.id.trim()
|
|
186
|
+
: "";
|
|
122
187
|
if (!name) {
|
|
123
188
|
throw new Error(`Agent ${agent.id} has an MCP server entry without a name`);
|
|
124
189
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
190
|
+
let serverId = referencedServer?.id;
|
|
191
|
+
let parsedServer = referencedServer;
|
|
192
|
+
if (!parsedServer) {
|
|
193
|
+
serverId = `${agent.id}.${name}`;
|
|
194
|
+
parsedServer = parseMcpServerObject({
|
|
195
|
+
id: serverId,
|
|
196
|
+
kind: "mcp",
|
|
197
|
+
sourcePath: agent.sourcePath,
|
|
198
|
+
value: item,
|
|
199
|
+
});
|
|
200
|
+
mcpServers.set(serverId, parsedServer);
|
|
201
|
+
}
|
|
202
|
+
else if (referencedServer && hasMcpServerUsageOverrides(item)) {
|
|
203
|
+
parsedServer = mergeReferencedMcpServer(referencedServer, item, agent.id, name, agent.sourcePath);
|
|
204
|
+
serverId = parsedServer.id;
|
|
205
|
+
mcpServers.set(serverId, parsedServer);
|
|
206
|
+
}
|
|
132
207
|
const filter = compileMcpToolFilter(item);
|
|
133
|
-
mcpServers.set(serverId, parsedServer);
|
|
134
208
|
const remoteTools = await listRemoteMcpTools(toMcpServerConfig(parsedServer));
|
|
135
209
|
for (const remoteTool of remoteTools) {
|
|
136
210
|
if (!shouldIncludeRemoteMcpTool(filter, remoteTool.name)) {
|
|
@@ -3,9 +3,31 @@ import { readdir } from "node:fs/promises";
|
|
|
3
3
|
import { parseAllDocuments } from "yaml";
|
|
4
4
|
import { fileExists, listFilesRecursive, readYamlOrJson } from "../utils/fs.js";
|
|
5
5
|
const MODEL_FILENAMES = ["models.yaml", "models.yml"];
|
|
6
|
+
const ENV_PLACEHOLDER_PATTERN = /\$\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
6
7
|
function asObject(value) {
|
|
7
8
|
return typeof value === "object" && value ? value : undefined;
|
|
8
9
|
}
|
|
10
|
+
function interpolateEnvString(value, sourcePath) {
|
|
11
|
+
return value.replaceAll(ENV_PLACEHOLDER_PATTERN, (_match, envName) => {
|
|
12
|
+
const resolved = process.env[envName];
|
|
13
|
+
if (typeof resolved !== "string" || resolved.length === 0) {
|
|
14
|
+
throw new Error(`YAML document ${sourcePath} references missing environment variable ${envName}`);
|
|
15
|
+
}
|
|
16
|
+
return resolved;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function interpolateEnvPlaceholders(value, sourcePath) {
|
|
20
|
+
if (typeof value === "string") {
|
|
21
|
+
return interpolateEnvString(value, sourcePath);
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
return value.map((entry) => interpolateEnvPlaceholders(entry, sourcePath));
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === "object" && value !== null) {
|
|
27
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, interpolateEnvPlaceholders(entry, sourcePath)]));
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
9
31
|
function normalizeCatalogSpec(document, options = {}) {
|
|
10
32
|
const typed = asObject(document);
|
|
11
33
|
const spec = typed?.spec;
|
|
@@ -116,15 +138,17 @@ async function objectItemsFromDocument(document, sourcePath) {
|
|
|
116
138
|
: undefined;
|
|
117
139
|
const catalogItems = catalogKind === "Models"
|
|
118
140
|
? normalizeCatalogSpec(document, { defaultKind: "Model" })
|
|
119
|
-
: catalogKind === "
|
|
120
|
-
? normalizeCatalogSpec(document)
|
|
121
|
-
: catalogKind === "
|
|
122
|
-
? normalizeCatalogSpec(document
|
|
123
|
-
: catalogKind === "
|
|
124
|
-
? normalizeCatalogSpec(document, { defaultKind: "
|
|
125
|
-
: catalogKind === "
|
|
126
|
-
? normalizeCatalogSpec(document, { defaultKind: "
|
|
127
|
-
:
|
|
141
|
+
: catalogKind === "EmbeddingModels"
|
|
142
|
+
? normalizeCatalogSpec(document, { defaultKind: "EmbeddingModel" })
|
|
143
|
+
: catalogKind === "Stores"
|
|
144
|
+
? normalizeCatalogSpec(document)
|
|
145
|
+
: catalogKind === "Backends"
|
|
146
|
+
? normalizeCatalogSpec(document, { defaultKind: "Backend" })
|
|
147
|
+
: catalogKind === "Tools"
|
|
148
|
+
? normalizeCatalogSpec(document, { defaultKind: "Tool" })
|
|
149
|
+
: catalogKind === "McpServers"
|
|
150
|
+
? normalizeCatalogSpec(document, { defaultKind: "McpServer" })
|
|
151
|
+
: [];
|
|
128
152
|
if (catalogItems.length > 0) {
|
|
129
153
|
return catalogItems;
|
|
130
154
|
}
|
|
@@ -148,7 +172,8 @@ export async function readYamlItems(root, relativeDir, options = {}) {
|
|
|
148
172
|
for (const filePath of files) {
|
|
149
173
|
const parsedDocuments = parseAllDocuments(await readYamlOrJson(filePath));
|
|
150
174
|
for (const parsedDocument of parsedDocuments) {
|
|
151
|
-
|
|
175
|
+
const resolvedDocument = interpolateEnvPlaceholders(parsedDocument.toJSON(), filePath);
|
|
176
|
+
for (const item of await objectItemsFromDocument(resolvedDocument, filePath)) {
|
|
152
177
|
records.push({ item: normalizeYamlItem(item), sourcePath: filePath });
|
|
153
178
|
}
|
|
154
179
|
}
|
|
@@ -164,7 +189,8 @@ export async function readNamedYamlItems(root, filenames) {
|
|
|
164
189
|
}
|
|
165
190
|
const parsedDocuments = parseAllDocuments(await readYamlOrJson(filePath));
|
|
166
191
|
for (const parsedDocument of parsedDocuments) {
|
|
167
|
-
|
|
192
|
+
const resolvedDocument = interpolateEnvPlaceholders(parsedDocument.toJSON(), filePath);
|
|
193
|
+
for (const item of await objectItemsFromDocument(resolvedDocument, filePath)) {
|
|
168
194
|
records.push({ item: normalizeYamlItem(item), sourcePath: filePath });
|
|
169
195
|
}
|
|
170
196
|
}
|
|
@@ -190,7 +216,8 @@ export async function readNamedModelItems(root) {
|
|
|
190
216
|
for (const filePath of [...filePaths].sort()) {
|
|
191
217
|
const parsedDocuments = parseAllDocuments(await readYamlOrJson(filePath));
|
|
192
218
|
for (const parsedDocument of parsedDocuments) {
|
|
193
|
-
|
|
219
|
+
const resolvedDocument = interpolateEnvPlaceholders(parsedDocument.toJSON(), filePath);
|
|
220
|
+
for (const item of await objectItemsFromDocument(resolvedDocument, filePath)) {
|
|
194
221
|
const normalized = normalizeYamlItem(item);
|
|
195
222
|
if (normalized.kind === "model" && typeof normalized.id === "string" && normalized.id.trim()) {
|
|
196
223
|
records.push({ item: normalized, sourcePath: filePath });
|