@botbotgo/agent-harness 0.0.134 → 0.0.136

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 (32) hide show
  1. package/README.md +110 -49
  2. package/README.zh.md +102 -49
  3. package/dist/config/agents/direct.yaml +70 -71
  4. package/dist/config/agents/orchestra.yaml +90 -91
  5. package/dist/contracts/workspace.d.ts +12 -2
  6. package/dist/extensions.js +13 -1
  7. package/dist/init-project.js +19 -21
  8. package/dist/package-version.d.ts +1 -1
  9. package/dist/package-version.js +1 -1
  10. package/dist/resource/mcp-tool-support.d.ts +4 -0
  11. package/dist/resource/mcp-tool-support.js +112 -35
  12. package/dist/resource/resource-impl.js +199 -7
  13. package/dist/runtime/adapter/runtime-shell.d.ts +3 -1
  14. package/dist/runtime/adapter/runtime-shell.js +2 -1
  15. package/dist/runtime/adapter/tool/tool-arguments.js +1 -0
  16. package/dist/runtime/adapter/tool/tool-hitl.js +3 -0
  17. package/dist/runtime/agent-runtime-adapter.d.ts +6 -0
  18. package/dist/runtime/agent-runtime-adapter.js +32 -2
  19. package/dist/runtime/harness.js +2 -0
  20. package/dist/tool-modules.d.ts +5 -0
  21. package/dist/tool-modules.js +10 -0
  22. package/dist/workspace/agent-binding-compiler.d.ts +2 -2
  23. package/dist/workspace/agent-binding-compiler.js +78 -6
  24. package/dist/workspace/compile.js +150 -6
  25. package/dist/workspace/object-loader.js +148 -53
  26. package/dist/workspace/resource-compilers.js +6 -0
  27. package/dist/workspace/support/source-collectors.js +1 -1
  28. package/dist/workspace/support/workspace-ref-utils.d.ts +1 -0
  29. package/dist/workspace/support/workspace-ref-utils.js +9 -0
  30. package/dist/workspace/tool-hydration.js +87 -13
  31. package/dist/workspace/yaml-object-reader.js +40 -13
  32. package/package.json +1 -1
@@ -1,11 +1,16 @@
1
+ import path from "node:path";
2
+ import { existsSync } from "node:fs";
3
+ import { readdir } from "node:fs/promises";
1
4
  import { ensureResourceSources } from "../resource/resource.js";
2
- import { loadWorkspaceObjects } from "./object-loader.js";
5
+ import { ensureExternalResourceSource, isExternalSourceLocator, resolveResourcePackageRoot } from "../resource/sources.js";
6
+ import { loadWorkspaceObjects, readToolModuleItems, readYamlItems } from "./object-loader.js";
7
+ import { validateSkillMetadata } from "../runtime/support/skill-metadata.js";
3
8
  import { parseEmbeddingModelObject, parseMcpServerObject, parseModelObject, parseToolObject, parseVectorStoreObject, validateEmbeddingModelObject, validateMcpServerObject, validateModelObject, validateToolObject, validateVectorStoreObject, } from "./resource-compilers.js";
4
9
  import { validateAgent, validateTopology } from "./validate.js";
5
10
  import { compileBinding } from "./agent-binding-compiler.js";
6
11
  import { discoverSubagents, ensureDiscoverySources } from "./support/discovery.js";
7
12
  import { collectAgentDiscoverySourceRefs, collectToolSourceRefs } from "./support/source-collectors.js";
8
- import { getRoutingDefaultAgentId, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
13
+ import { getRoutingDefaultAgentId, getRuntimeResources, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
9
14
  import { hydrateAgentMcpTools, hydrateResourceAndExternalTools } from "./tool-hydration.js";
10
15
  function collectParsedResources(refs) {
11
16
  const embeddings = new Map();
@@ -42,6 +47,17 @@ function validateWorkspaceResources(embeddings, mcpServers, models, vectorStores
42
47
  models.forEach((model) => validateModelObject(model, models));
43
48
  vectorStores.forEach((vectorStore) => validateVectorStoreObject(vectorStore));
44
49
  tools.forEach((tool) => validateToolObject(tool, tools));
50
+ tools.forEach((tool) => {
51
+ if (!tool.embeddingModelRef) {
52
+ return;
53
+ }
54
+ const embeddingModelId = tool.embeddingModelRef.startsWith("embedding-model/")
55
+ ? tool.embeddingModelRef.slice("embedding-model/".length)
56
+ : tool.embeddingModelRef;
57
+ if (!embeddings.has(embeddingModelId)) {
58
+ throw new Error(`Tool ${tool.id} references missing embedding model ${tool.embeddingModelRef}`);
59
+ }
60
+ });
45
61
  agents.forEach(validateAgent);
46
62
  validateTopology(agents);
47
63
  tools.forEach((tool) => {
@@ -82,6 +98,109 @@ function validateRoutingTargets(refs, agentsList) {
82
98
  }
83
99
  }
84
100
  }
101
+ function resolveLocalResourceRoot(candidate, workspaceRoot) {
102
+ const resolved = path.resolve(workspaceRoot, candidate);
103
+ const resourceRoot = resolveResourcePackageRoot(resolved);
104
+ if (!resourceRoot) {
105
+ throw new Error(`Workspace resource ${candidate} is missing resources/package.json.`);
106
+ }
107
+ return resourceRoot;
108
+ }
109
+ async function resolveConfiguredResources(entries, workspaceRoot) {
110
+ const resolved = [];
111
+ for (const entry of entries) {
112
+ const key = entry.trim();
113
+ if (!key) {
114
+ continue;
115
+ }
116
+ const root = isExternalSourceLocator(key)
117
+ ? await ensureExternalResourceSource(key, workspaceRoot)
118
+ : resolveLocalResourceRoot(key, workspaceRoot);
119
+ resolved.push({ key, root });
120
+ }
121
+ return resolved;
122
+ }
123
+ async function registerAttachedResourceTools(tools, resourceRoot) {
124
+ const toolsRoot = path.join(resourceRoot, "tools");
125
+ for (const { item, sourcePath } of await readYamlItems(toolsRoot, undefined, { recursive: true })) {
126
+ const parsed = parseToolObject({
127
+ id: typeof item.id === "string" ? item.id : path.basename(sourcePath).replace(/\.(yaml|yml|json)$/i, ""),
128
+ kind: "tool",
129
+ sourcePath,
130
+ value: item,
131
+ });
132
+ tools.set(parsed.id, parsed);
133
+ }
134
+ for (const { item, sourcePath } of await readToolModuleItems(toolsRoot)) {
135
+ const parsed = parseToolObject({
136
+ id: String(item.id),
137
+ kind: "tool",
138
+ sourcePath,
139
+ value: item,
140
+ });
141
+ tools.set(parsed.id, parsed);
142
+ }
143
+ }
144
+ async function collectSkillRoots(root) {
145
+ try {
146
+ const entries = await readdir(root, { withFileTypes: true });
147
+ const skillDocument = entries.find((entry) => entry.isFile() && entry.name === "SKILL.md");
148
+ if (skillDocument) {
149
+ return [root];
150
+ }
151
+ const nested = await Promise.all(entries
152
+ .filter((entry) => entry.isDirectory())
153
+ .map((entry) => collectSkillRoots(path.join(root, entry.name))));
154
+ return nested.flat();
155
+ }
156
+ catch {
157
+ return [];
158
+ }
159
+ }
160
+ async function registerWorkspaceSkillRegistry(skillCollectionRoots) {
161
+ const registry = new Map();
162
+ for (const skillsRoot of skillCollectionRoots) {
163
+ for (const skillRoot of await collectSkillRoots(skillsRoot)) {
164
+ const metadata = validateSkillMetadata(skillRoot);
165
+ const existing = registry.get(metadata.name);
166
+ if (existing && existing !== skillRoot) {
167
+ throw new Error(`Duplicate skill name ${metadata.name} found in ${existing} and ${skillRoot}`);
168
+ }
169
+ registry.set(metadata.name, skillRoot);
170
+ }
171
+ }
172
+ return registry;
173
+ }
174
+ function validateToolNameConflicts(tools) {
175
+ const seen = new Map();
176
+ for (const tool of tools.values()) {
177
+ const existing = seen.get(tool.name);
178
+ if (existing && existing !== tool.sourcePath) {
179
+ throw new Error(`Duplicate tool name ${tool.name} found in ${existing} and ${tool.sourcePath}`);
180
+ }
181
+ seen.set(tool.name, tool.sourcePath);
182
+ }
183
+ }
184
+ function resolveAgentSkillNames(agents, skillRegistry, skillCollectionRoots) {
185
+ for (const agent of agents) {
186
+ agent.skillPathRefs = agent.skillPathRefs.map((entry) => {
187
+ const registered = skillRegistry.get(entry);
188
+ if (registered) {
189
+ return registered;
190
+ }
191
+ if (path.isAbsolute(entry) || entry.includes("/") || entry.startsWith("builtin://")) {
192
+ return entry;
193
+ }
194
+ for (const skillsRoot of skillCollectionRoots) {
195
+ const candidate = path.join(skillsRoot, entry);
196
+ if (existsSync(path.join(candidate, "SKILL.md"))) {
197
+ return candidate;
198
+ }
199
+ }
200
+ return entry;
201
+ });
202
+ }
203
+ }
85
204
  export async function loadWorkspace(workspaceRoot, options = {}) {
86
205
  const loaded = await loadWorkspaceObjects(workspaceRoot, options);
87
206
  loaded.agents = await discoverSubagents(loaded.agents, workspaceRoot);
@@ -92,14 +211,39 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
92
211
  }
93
212
  const { embeddings, mcpServers, models, vectorStores, tools } = collectParsedResources(loaded.refs);
94
213
  await hydrateAgentMcpTools(loaded.agents, mcpServers, tools);
95
- const toolSourceRefs = collectToolSourceRefs(tools, loaded.agents, options);
96
- await ensureResourceSources(toolSourceRefs, workspaceRoot);
97
- await hydrateResourceAndExternalTools(tools, toolSourceRefs, workspaceRoot);
214
+ const configuredResources = Array.from(new Set([
215
+ ...getRuntimeResources(loaded.refs),
216
+ ...(options.resources ?? []),
217
+ ]));
218
+ const resolvedConfiguredResources = await resolveConfiguredResources(configuredResources, workspaceRoot);
219
+ for (const resource of resolvedConfiguredResources) {
220
+ await registerAttachedResourceTools(tools, resource.root);
221
+ }
222
+ const localResourceRoot = resolveResourcePackageRoot(workspaceRoot);
223
+ const skillCollectionRoots = [
224
+ path.join(workspaceRoot, "modules", "skills"),
225
+ ...(localResourceRoot ? [path.join(localResourceRoot, "skills")] : []),
226
+ ...resolvedConfiguredResources.map((resource) => path.join(resource.root, "skills")),
227
+ ];
228
+ const skillRegistry = await registerWorkspaceSkillRegistry(skillCollectionRoots);
229
+ resolveAgentSkillNames(loaded.agents, skillRegistry, skillCollectionRoots);
230
+ const collectedResources = collectToolSourceRefs(tools, loaded.agents, {
231
+ ...options,
232
+ resources: configuredResources,
233
+ });
234
+ const externalResources = collectedResources.filter((resource) => isExternalSourceLocator(resource));
235
+ await ensureResourceSources(externalResources, workspaceRoot);
236
+ await hydrateResourceAndExternalTools(tools, externalResources, workspaceRoot);
237
+ validateToolNameConflicts(tools);
238
+ const resources = Array.from(new Set([
239
+ ...(localResourceRoot ? [localResourceRoot] : []),
240
+ ...collectedResources,
241
+ ]));
98
242
  validateWorkspaceResources(embeddings, mcpServers, models, vectorStores, tools, loaded.agents);
99
243
  validateRoutingTargets(loaded.refs, loaded.agents);
100
244
  return {
101
245
  workspaceRoot,
102
- resourceSources: [...toolSourceRefs],
246
+ resources,
103
247
  refs: loaded.refs,
104
248
  embeddings,
105
249
  mcpServers,
@@ -93,6 +93,55 @@ function readRefArray(items) {
93
93
  : undefined)
94
94
  .filter((item) => Boolean(item));
95
95
  }
96
+ function normalizeToolUsageOverrides(value) {
97
+ const record = asMutableObject(value);
98
+ if (!record) {
99
+ return undefined;
100
+ }
101
+ const directKeys = new Set(["config", "hitl", "retryable", "subprocess", "embeddingModelRef"]);
102
+ const directOverrides = {};
103
+ const configOverrides = {};
104
+ for (const [key, entry] of Object.entries(record)) {
105
+ if (directKeys.has(key)) {
106
+ directOverrides[key] = cloneConfigValue(entry);
107
+ continue;
108
+ }
109
+ configOverrides[key] = cloneConfigValue(entry);
110
+ }
111
+ return {
112
+ ...directOverrides,
113
+ ...(Object.keys(configOverrides).length > 0
114
+ ? {
115
+ config: {
116
+ ...(asMutableObject(directOverrides.config) ?? {}),
117
+ ...configOverrides,
118
+ },
119
+ }
120
+ : {}),
121
+ };
122
+ }
123
+ function readToolBindingArray(items) {
124
+ return toArray(items)
125
+ .map((item) => {
126
+ if (typeof item === "string") {
127
+ return { ref: item };
128
+ }
129
+ if (typeof item !== "object" || !item || Array.isArray(item)) {
130
+ return undefined;
131
+ }
132
+ const entries = Object.entries(item);
133
+ if (entries.length !== 1 || typeof entries[0]?.[0] !== "string") {
134
+ throw new Error("Agent tools entries must be either a tool name string or a single-key override object");
135
+ }
136
+ const [ref, rawOverride] = entries[0];
137
+ const overrides = normalizeToolUsageOverrides(rawOverride);
138
+ return {
139
+ ref,
140
+ ...(overrides ? { overrides } : {}),
141
+ };
142
+ })
143
+ .filter((item) => Boolean(item));
144
+ }
96
145
  function readPathArray(items) {
97
146
  return toArray(items)
98
147
  .map((item) => typeof item === "string"
@@ -102,6 +151,11 @@ function readPathArray(items) {
102
151
  : undefined)
103
152
  .filter((item) => Boolean(item));
104
153
  }
154
+ function readStringArray(items) {
155
+ return toArray(items)
156
+ .filter((item) => typeof item === "string" && item.trim().length > 0)
157
+ .map((item) => item.trim());
158
+ }
105
159
  function readSingleRef(value) {
106
160
  if (typeof value === "string" && value.trim()) {
107
161
  return value;
@@ -151,7 +205,16 @@ const CONSUMED_AGENT_CONFIG_KEYS = [
151
205
  "generalPurposeAgent",
152
206
  "filesystem",
153
207
  ];
154
- const RESERVED_EXECUTION_KEYS = [
208
+ const NON_AGENT_CONFIG_ITEM_KEYS = [
209
+ "id",
210
+ "kind",
211
+ "description",
212
+ "capabilities",
213
+ "runtime",
214
+ "executionMode",
215
+ "sourcePath",
216
+ ];
217
+ const RESERVED_AGENT_KEYS = [
155
218
  "backend",
156
219
  "modelRef",
157
220
  "tools",
@@ -161,7 +224,7 @@ const RESERVED_EXECUTION_KEYS = [
161
224
  "mcpServers",
162
225
  "config",
163
226
  ];
164
- const MIGRATED_EXECUTION_CONFIG_KEYS = [
227
+ const MIGRATED_AGENT_CONFIG_KEYS = [
165
228
  "systemPrompt",
166
229
  "checkpointer",
167
230
  "interruptOn",
@@ -176,23 +239,19 @@ const MIGRATED_EXECUTION_CONFIG_KEYS = [
176
239
  "generalPurposeAgent",
177
240
  "filesystem",
178
241
  ];
179
- function readExecutionConfig(value) {
180
- return asMutableObject(value);
181
- }
182
242
  function normalizeAgentItemForMerge(item) {
183
243
  const normalized = { ...item };
184
- const execution = readExecutionConfig(normalized.execution);
185
- if (!execution) {
186
- return normalized;
244
+ if (normalized.execution !== undefined) {
245
+ throw new Error("Agent spec.execution is no longer supported; move backend, modelRef, tools, skills, memory, subagents, mcpServers, and config directly under spec");
187
246
  }
188
- const config = asMutableObject(execution.config);
247
+ const config = asMutableObject(normalized.config);
189
248
  const runtime = readRuntimeConfig(normalized);
190
249
  if (config) {
191
- for (const key of MIGRATED_EXECUTION_CONFIG_KEYS) {
192
- if (!(key in config) || execution[key] !== undefined) {
250
+ for (const key of MIGRATED_AGENT_CONFIG_KEYS) {
251
+ if (!(key in config) || normalized[key] !== undefined) {
193
252
  continue;
194
253
  }
195
- execution[key] = cloneConfigValue(config[key]);
254
+ normalized[key] = cloneConfigValue(config[key]);
196
255
  delete config[key];
197
256
  }
198
257
  if (config.runtimeMemory !== undefined && runtime?.runtimeMemory === undefined) {
@@ -203,26 +262,24 @@ function normalizeAgentItemForMerge(item) {
203
262
  }
204
263
  }
205
264
  if (config && Object.keys(config).length > 0) {
206
- execution.config = config;
265
+ normalized.config = config;
207
266
  }
208
267
  else {
209
- delete execution.config;
268
+ delete normalized.config;
210
269
  }
211
- normalized.execution = execution;
212
270
  return normalized;
213
271
  }
214
272
  function readExecutionAgentConfig(item) {
215
- const execution = readExecutionConfig(item.execution) ?? {};
216
- const config = asMutableObject(execution.config) ?? {};
217
- const directExecutionConfig = Object.fromEntries(Object.entries(execution).filter(([key]) => !RESERVED_EXECUTION_KEYS.includes(key)));
273
+ const config = asMutableObject(item.config) ?? {};
274
+ const directExecutionConfig = Object.fromEntries(Object.entries(item).filter(([key]) => !RESERVED_AGENT_KEYS.includes(key) &&
275
+ !NON_AGENT_CONFIG_ITEM_KEYS.includes(key)));
218
276
  return {
219
277
  ...config,
220
278
  ...directExecutionConfig,
221
279
  };
222
280
  }
223
281
  function readExecutionValue(item, key, reader) {
224
- const execution = readExecutionConfig(item.execution);
225
- return reader(execution?.[key]);
282
+ return reader(item[key]);
226
283
  }
227
284
  function readRuntimeConfig(item) {
228
285
  return asMutableObject(item.runtime);
@@ -285,13 +342,22 @@ function readPassthroughConfig(item, consumedKeys) {
285
342
  return Object.keys(passthrough).length > 0 ? passthrough : undefined;
286
343
  }
287
344
  function resolveExecutionBackend(item, current) {
288
- const execution = readExecutionConfig(item.execution) ?? readExecutionConfig(current?.execution);
289
- if (typeof execution?.mode === "string" && execution.mode.trim().length > 0) {
290
- throw new Error("Agent execution.mode is no longer supported; use execution.backend");
291
- }
292
- const backend = typeof execution?.backend === "string"
293
- ? execution.backend.trim().toLowerCase()
294
- : undefined;
345
+ if (item.execution !== undefined || current?.execution !== undefined) {
346
+ throw new Error("Agent spec.execution is no longer supported; move backend, modelRef, tools, skills, memory, subagents, mcpServers, and config directly under spec");
347
+ }
348
+ const mode = typeof item.mode === "string"
349
+ ? item.mode
350
+ : typeof current?.mode === "string"
351
+ ? current.mode
352
+ : undefined;
353
+ if (typeof mode === "string" && mode.trim().length > 0) {
354
+ throw new Error("Agent mode is no longer supported; use backend");
355
+ }
356
+ const backend = typeof item.backend === "string"
357
+ ? item.backend.trim().toLowerCase()
358
+ : typeof current?.backend === "string"
359
+ ? current.backend.trim().toLowerCase()
360
+ : undefined;
295
361
  if (backend === "langchain-v1") {
296
362
  return "langchain-v1";
297
363
  }
@@ -299,7 +365,7 @@ function resolveExecutionBackend(item, current) {
299
365
  return "deepagent";
300
366
  }
301
367
  if (backend === "langgraph") {
302
- throw new Error("Agent execution.backend=langgraph is no longer supported; use execution.backend=langchain-v1 or execution.backend=deepagent");
368
+ throw new Error("Agent backend=langgraph is no longer supported; use backend=langchain-v1 or backend=deepagent");
303
369
  }
304
370
  return undefined;
305
371
  }
@@ -345,29 +411,33 @@ function readAgentConfig(item, options = {}) {
345
411
  };
346
412
  }
347
413
  export function parseAgentItem(item, sourcePath) {
414
+ const normalizedItem = normalizeAgentItemForMerge(item);
348
415
  const moduleRoot = moduleRootForSourcePath(sourcePath, "agents");
349
- const subagentRefs = readExecutionValue(item, "subagents", readRefArray);
350
- const subagentPathRefs = readExecutionValue(item, "subagents", readPathArray).map((entry) => resolveModuleRelativePath(entry, moduleRoot));
351
- const executionMode = String(resolveExecutionBackend(item) ?? "deepagent");
352
- const runtime = readRuntimeConfig(item);
416
+ const subagentRefs = readExecutionValue(normalizedItem, "subagents", readRefArray);
417
+ const toolBindings = readExecutionValue(normalizedItem, "tools", readToolBindingArray);
418
+ const subagentPathRefs = readExecutionValue(normalizedItem, "subagents", readPathArray).map((entry) => resolveModuleRelativePath(entry, moduleRoot));
419
+ const executionMode = String(resolveExecutionBackend(normalizedItem) ?? "deepagent");
420
+ const runtime = readRuntimeConfig(normalizedItem);
353
421
  return {
354
- id: String(item.id),
422
+ id: String(normalizedItem.id),
355
423
  executionMode: executionMode,
356
- runtimeMemory: readRuntimeMemoryConfig(item, runtime),
357
- capabilities: readCapabilities(item.capabilities) ?? (executionMode === "deepagent"
424
+ runtimeMemory: readRuntimeMemoryConfig(normalizedItem, runtime),
425
+ capabilities: readCapabilities(normalizedItem.capabilities) ?? (executionMode === "deepagent"
358
426
  ? { delegation: true, memory: true }
359
427
  : { delegation: true, memory: true }),
360
- description: String(item.description ?? ""),
361
- modelRef: readExecutionValue(item, "modelRef", readSingleRef) ?? "",
428
+ description: String(normalizedItem.description ?? ""),
429
+ modelRef: readExecutionValue(normalizedItem, "modelRef", readSingleRef) ?? "",
362
430
  runRoot: typeof runtime?.runRoot === "string" ? runtime.runRoot : undefined,
363
- toolRefs: readExecutionValue(item, "tools", readRefArray),
364
- mcpServers: readExecutionValue(item, "mcpServers", readObjectArray),
365
- skillPathRefs: readExecutionValue(item, "skills", readPathArray).map((entry) => resolveModuleRelativePath(entry, moduleRoot)),
366
- memorySources: readExecutionValue(item, "memory", readPathArray).map((entry) => resolveModuleRelativePath(entry, moduleRoot)),
431
+ toolRefs: toolBindings.map((binding) => binding.ref),
432
+ toolBindings,
433
+ inlineTools: undefined,
434
+ mcpServers: readExecutionValue(normalizedItem, "mcpServers", readObjectArray),
435
+ skillPathRefs: readExecutionValue(normalizedItem, "skills", readStringArray),
436
+ memorySources: readExecutionValue(normalizedItem, "memory", readPathArray).map((entry) => resolveModuleRelativePath(entry, moduleRoot)),
367
437
  subagentRefs,
368
438
  subagentPathRefs,
369
- langchainAgentConfig: normalizeModuleAgentConfig(readAgentConfig(item, { includeDelegationControls: true }), moduleRoot),
370
- deepAgentConfig: normalizeModuleAgentConfig(readAgentConfig(item, {
439
+ langchainAgentConfig: normalizeModuleAgentConfig(readAgentConfig(normalizedItem, { includeDelegationControls: true }), moduleRoot),
440
+ deepAgentConfig: normalizeModuleAgentConfig(readAgentConfig(normalizedItem, {
371
441
  includeObjectBackend: true,
372
442
  includeDelegationControls: false,
373
443
  }), moduleRoot),
@@ -409,9 +479,19 @@ function mergeValues(base, override) {
409
479
  }
410
480
  function mergeRawItemRecord(records, key, item, sourcePath) {
411
481
  const current = records.get(key);
482
+ const mergedItem = current ? mergeValues(current.item, item) : item;
483
+ const kind = typeof mergedItem.kind === "string" ? mergedItem.kind : undefined;
484
+ const type = typeof mergedItem.type === "string" ? mergedItem.type : undefined;
485
+ const resolvedSourcePath = current &&
486
+ kind === "tool" &&
487
+ type === "function" &&
488
+ isSupportedToolModulePath(current.sourcePath) &&
489
+ !isSupportedToolModulePath(sourcePath)
490
+ ? current.sourcePath
491
+ : sourcePath;
412
492
  const mergedRecord = {
413
- item: current ? mergeValues(current.item, item) : item,
414
- sourcePath,
493
+ item: mergedItem,
494
+ sourcePath: resolvedSourcePath,
415
495
  };
416
496
  records.set(key, mergedRecord);
417
497
  return mergedRecord;
@@ -463,7 +543,7 @@ async function loadConventionalObjectsForRoot(root, mergedObjects) {
463
543
  for (const objectRoot of conventionalDirectoryRoots(root, directory)) {
464
544
  for (const { item, sourcePath } of await readYamlItems(objectRoot, undefined, { recursive: true })) {
465
545
  const workspaceObject = parseWorkspaceObject(item, sourcePath);
466
- if (!workspaceObject || workspaceObject.kind === "tool") {
546
+ if (!workspaceObject) {
467
547
  continue;
468
548
  }
469
549
  mergeWorkspaceObjectRecord(mergedObjects, workspaceObject, item, sourcePath);
@@ -567,24 +647,39 @@ function isAgentKind(kind) {
567
647
  return kind === "agent";
568
648
  }
569
649
  async function readConfigAgentItems(configRoot) {
570
- const records = await readYamlItems(configRoot, "agents", { recursive: true });
650
+ const records = await readYamlItems(configRoot, undefined, { recursive: true });
571
651
  return records.filter(({ item, sourcePath }) => {
572
652
  const kind = typeof item.kind === "string" ? item.kind : undefined;
573
653
  if (!isAgentKind(kind)) {
574
654
  return false;
575
655
  }
576
- return sourcePath.includes(`${path.sep}agents${path.sep}`);
656
+ const relativePath = path.relative(configRoot, sourcePath);
657
+ if (!relativePath || relativePath.startsWith("..")) {
658
+ return false;
659
+ }
660
+ return !relativePath.includes(path.sep) || relativePath.startsWith(`agents${path.sep}`);
577
661
  });
578
662
  }
579
663
  export async function readToolModuleItems(root) {
580
664
  if (!(await fileExists(root))) {
581
665
  return [];
582
666
  }
583
- const entries = await readdir(root, { withFileTypes: true });
584
- const files = entries
585
- .filter((entry) => entry.isFile() && isSupportedToolModulePath(entry.name))
586
- .map((entry) => path.join(root, entry.name))
587
- .sort();
667
+ const files = [];
668
+ const pending = [root];
669
+ while (pending.length > 0) {
670
+ const current = pending.shift();
671
+ const entries = await readdir(current, { withFileTypes: true });
672
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
673
+ const entryPath = path.join(current, entry.name);
674
+ if (entry.isDirectory()) {
675
+ pending.push(entryPath);
676
+ continue;
677
+ }
678
+ if (entry.isFile() && isSupportedToolModulePath(entry.name)) {
679
+ files.push(entryPath);
680
+ }
681
+ }
682
+ }
588
683
  const records = [];
589
684
  for (const filePath of files) {
590
685
  const sourceText = await readFile(filePath, "utf8");
@@ -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"
@@ -1,6 +1,6 @@
1
1
  import { isExternalSourceLocator } from "../../resource/sources.js";
2
2
  export function collectToolSourceRefs(tools, agents, options) {
3
- const refs = new Set(options.resourceSources ?? []);
3
+ const refs = new Set(options.resources ?? []);
4
4
  for (const tool of tools.values()) {
5
5
  for (const ref of tool.bundleRefs) {
6
6
  if (isExternalSourceLocator(ref)) {
@@ -33,6 +33,7 @@ export type ResilienceConfig = {
33
33
  };
34
34
  export declare function getWorkspaceObject(refs: Map<string, WorkspaceObject | ParsedAgentObject>, ref: string | undefined): WorkspaceObject | undefined;
35
35
  export declare function getRuntimeDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
36
+ export declare function getRuntimeResources(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string[];
36
37
  export declare function getRuntimeMemoryDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
37
38
  export declare function getRecoveryConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): RecoveryConfig;
38
39
  export declare function getConcurrencyConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): ConcurrencyConfig;
@@ -26,6 +26,15 @@ export function getRuntimeDefaults(refs) {
26
26
  }
27
27
  return runtimes[0].value;
28
28
  }
29
+ export function getRuntimeResources(refs) {
30
+ const runtimeDefaults = getRuntimeDefaults(refs);
31
+ if (!Array.isArray(runtimeDefaults?.resources)) {
32
+ return [];
33
+ }
34
+ return runtimeDefaults.resources
35
+ .filter((value) => typeof value === "string" && value.trim().length > 0)
36
+ .map((value) => value.trim());
37
+ }
29
38
  export function getRuntimeMemoryDefaults(refs) {
30
39
  const runtimeMemories = Array.from(refs.values()).filter((object) => !("executionMode" in object) && object.kind === "runtime-memory");
31
40
  if (runtimeMemories.length === 0) {